Datenbankarchitektur¶
KanjiIQ verwendet PostgreSQL 15 als primären Datenspeicher, bereitgestellt innerhalb des Kubernetes-Clusters mit persistentem Speicher.
Schemaübersicht¶
erDiagram
users ||--o{ study_sessions : has
users ||--o{ quiz_results : has
users ||--o{ test_results : has
kanji ||--o{ quiz_results : referenced_in
vocabulary ||--o{ quiz_results : referenced_in
users {
uuid id PK
text email
text password_hash
jsonb preferences
jsonb stats
timestamp created_at
}
kanji {
uuid id PK
text character
int jlpt_level
jsonb meanings
jsonb readings
text example_sentences
}
vocabulary {
uuid id PK
text expression
text reading
int jlpt_level
jsonb meanings
text part_of_speech
}
locale_configs {
uuid id PK
text locale_code
text[] default_languages
text[] available_languages
}
regional_analytics {
uuid id PK
text country_code
text request_path
text user_agent
text device_type
boolean is_suspicious
int response_status
timestamp created_at
}
ip_blocklist {
uuid id PK
text ip_address
text reason
text blocked_by
timestamp expires_at
boolean is_active
}
Mehrsprachige Inhaltsspeicherung¶
KanjiIQ speichert Übersetzungen mit PostgreSQL-JSONB-Spalten anstelle separater Übersetzungstabellen. Dies bietet flexible, schemalose mehrsprachige Speicherung:
// kanji.meanings column
{
"en": "mountain",
"es": "montaña",
"fr": "montagne",
"ja": "やま",
"pt": "montanha",
"ar": "جبل",
"zh-CN": "山"
}
Warum JSONB?
- Keine Schemamigration erforderlich beim Hinzufügen neuer Sprachen
- Eine einzige Abfrage ruft alle Übersetzungen für ein Kanji ab
- PostgreSQL JSONB-Operatoren ermöglichen effiziente sprachspezifische Abfragen
- GIN-Indizierung verfügbar für Volltextsuche über Übersetzungen hinweg
Kubernetes-Speicher¶
PostgreSQL läuft als Kubernetes Deployment mit einem PersistentVolumeClaim:
# k8s/02-postgres-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: jlpt-kanji
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Die Datenbank ist nicht außerhalb des Clusters erreichbar — nur der Backend-Pod kann sie über den jlpt-postgres-Service auf Port 5432 erreichen.
Wichtige Designentscheidungen¶
UUID-Primärschlüssel¶
Alle Tabellen verwenden UUID-Primärschlüssel anstelle von automatisch inkrementierenden Ganzzahlen. Dies unterstützt:
- Verteilte ID-Generierung (keine zentrale Sequenz)
- Sichere ID-Offenlegung in APIs (nicht erratbar)
- Zukünftige Multi-Region-Replikation ohne ID-Konflikte
Kaskadenlöschungen¶
Fremdschlüssel verwenden ON DELETE CASCADE, um die referenzielle Integrität zu wahren. Das Löschen eines Benutzers entfernt automatisch dessen Lernsitzungen, Quiz-Ergebnisse und Testergebnisse.
Benutzereinstellungen als JSONB¶
Benutzereinstellungen und Statistiken werden als JSONB statt als feste Spalten gespeichert:
// users.preferences
{
"defaultLanguages": ["en", "pt", "es"],
"studyLevels": ["N5", "N4"],
"showAllLanguages": false
}
// users.stats
{
"totalKanjiStudied": 245,
"averageScore": 78.5,
"streakDays": 12
}
Dies vermeidet Schemamigrationen für jede neue Einstellung oder Statistik, die der Anwendung hinzugefügt wird.
Backup-Strategie¶
Die Datenbank folgt der 3-2-1-Backup-Regel:
- 3 Kopien der Daten (Live + 2 Backups)
- 2 verschiedene Medien (PVC + Objektspeicher)
- 1 externe Kopie (Hetzner Object Storage)
Tägliche pg_dump-Exporte werden mit 30-tägiger Aufbewahrung gespeichert. Siehe den Deployment-Abschnitt für betriebliche Details.