Dokumentations-Updates: DATABASE_SCHEMA.md: - Version 0.3.4 - Migration 009 zur Historie hinzugefügt - target_groups Struktur korrigiert (M:N statt 1:N) - training_style_target_groups Junction-Tabelle dokumentiert - Backend API Endpoints dokumentiert (5 neue) - Frontend Admin-UI Änderungen dokumentiert DOMAIN_MODEL.md: - Version 0.3.4 - Dimension 3 (Zielgruppe) komplett überarbeitet - Hierarchischer Kontext aktualisiert (M:N Architektur) - Admin-UI Darstellung erklärt (Tree-View + Matrix + Global) - BREAKING CHANGE klar gekennzeichnet Fachlich: - Zielgruppen sind jetzt GLOBAL (nicht mehr hierarchisch) - M:N Zuordnung über Junction-Tabelle - Wiederverwendbarkeit: Eine Zielgruppe → mehrere Stile Version: 0.3.4
306 lines
8.9 KiB
Markdown
306 lines
8.9 KiB
Markdown
# Shinkan Jinkendo - Datenbank-Schema (Technisch)
|
|
|
|
**Version:** 0.3.4
|
|
**Stand:** 2026-04-23
|
|
**Aktuell deployed:** Migration 009 (Target Groups M:N Refactoring)
|
|
|
|
---
|
|
|
|
## Übersicht
|
|
|
|
Dieses Dokument beschreibt die **technische Datenbankstruktur** von Shinkan Jinkendo.
|
|
|
|
**Zuständig für fachliche Modellierung:** siehe `DOMAIN_MODEL.md`
|
|
|
|
---
|
|
|
|
## Migrations-Historie
|
|
|
|
| Nr. | Datum | Beschreibung | Status |
|
|
|-----|-------|--------------|--------|
|
|
| 001 | 2026-04-20 | Initial Schema (Auth, Profiles) | ✅ Deployed |
|
|
| 002 | 2026-04-20 | Clubs, Divisions, Groups | ✅ Deployed |
|
|
| 003 | 2026-04-20 | Skills, Methods | ✅ Deployed |
|
|
| 004 | 2026-04-21 | Training Types | ✅ Deployed |
|
|
| 005 | 2026-04-21 | Exercises (Basis) | ✅ Deployed |
|
|
| 006 | 2026-04-21 | Training Planning | ✅ Deployed |
|
|
| 007 | 2026-04-22 | Exercise Catalogs (idempotent) | ✅ Deployed |
|
|
| 008 | 2026-04-23 | M:N Exercise Relations + Hierarchical Catalogs | ✅ Deployed |
|
|
| 009 | 2026-04-23 | **Target Groups M:N Refactoring** (BREAKING) | ✅ Deployed |
|
|
|
|
---
|
|
|
|
## Migration 009: Target Groups M:N Refactoring (BREAKING CHANGE)
|
|
|
|
**Problem:** Zielgruppen waren hierarchisch an Trainingsstile gebunden (1:N via FK `training_style_id`).
|
|
**Lösung:** M:N Beziehung über Junction-Tabelle `training_style_target_groups`.
|
|
|
|
**Änderungen:**
|
|
- ❌ **Entfernt:** `target_groups.training_style_id` (FK constraint + column)
|
|
- ✅ **Neu:** `training_style_target_groups` Junction-Tabelle
|
|
- ✅ **Migriert:** Alte Zuordnungen zu M:N (alle mit `is_primary = true`)
|
|
- ✅ **Architektur:** Eine Zielgruppe kann mehreren Stilen zugeordnet sein
|
|
|
|
**Neue Struktur:**
|
|
```sql
|
|
-- Global unabhängige Zielgruppen (KEINE training_style_id mehr)
|
|
target_groups (
|
|
id, name, description, min_age, max_age,
|
|
sort_order, status, created_at, updated_at
|
|
)
|
|
|
|
-- M:N Junction-Tabelle
|
|
training_style_target_groups (
|
|
id SERIAL PRIMARY KEY,
|
|
training_style_id INT REFERENCES training_styles(id) ON DELETE CASCADE,
|
|
target_group_id INT REFERENCES target_groups(id) ON DELETE CASCADE,
|
|
is_primary BOOLEAN DEFAULT false,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(training_style_id, target_group_id)
|
|
)
|
|
```
|
|
|
|
**Backend API:** 5 neue Endpoints:
|
|
- `GET /api/training-style-target-groups` (Liste mit Filtern + Enrichment)
|
|
- `POST /api/training-style-target-groups` (Upsert-Logik)
|
|
- `PUT /api/training-style-target-groups/{id}` (is_primary Flag)
|
|
- `DELETE /api/training-style-target-groups/{id}`
|
|
- `GET /api/training-styles/hierarchy` (Hierarchische Struktur für Tree-View)
|
|
|
|
**Frontend:**
|
|
- Admin-UI Tab "Hierarchie" (Tree-View: Fokusbereich → Stil → Zielgruppen)
|
|
- Admin-UI Tab "Zuordnungen" (Checkbox-Matrix für M:N)
|
|
- Tab "Zielgruppen" überarbeitet (global, ohne training_style_id Dropdown)
|
|
|
|
---
|
|
|
|
## Kern-Entitäten (Stand Migration 007)
|
|
|
|
### Auth & Profile
|
|
|
|
```sql
|
|
profiles (id, name, email, password_hash, role, created_at)
|
|
sessions (id, profile_id, token, created_at, expires_at)
|
|
```
|
|
|
|
### Organisation
|
|
|
|
```sql
|
|
clubs (id, name, abbreviation, description, logo_url, ...)
|
|
divisions (id, club_id, name, description, ...)
|
|
training_groups (id, division_id, name, description, focus_area, level, ...)
|
|
```
|
|
|
|
### Kataloge (Einfache FK-Struktur - zu refactoren)
|
|
|
|
**ACHTUNG:** Aktuell 1:1 Beziehungen - fachlich inkorrekt!
|
|
|
|
```sql
|
|
focus_areas (id, name, abbreviation, description, color, icon, ...)
|
|
training_styles (id, name, abbreviation, description, parent_style_id, ...)
|
|
training_characters (id, name, description, ...)
|
|
skill_categories (id, name, description, parent_category_id, ...)
|
|
```
|
|
|
|
### Übungen (Aktuell - zu refactoren)
|
|
|
|
```sql
|
|
exercises (
|
|
id, title, summary, goal, execution, preparation, trainer_notes,
|
|
focus_area_id, -- ❌ 1:1 - sollte M:N sein
|
|
training_style_id, -- ❌ 1:1 - sollte M:N sein
|
|
training_character_id, -- ❌ 1:1 - sollte M:N sein
|
|
visibility, status, created_by, club_id, ...
|
|
)
|
|
|
|
exercise_skills (exercise_id, skill_id, is_primary, intensity, ...)
|
|
exercise_variants (id, exercise_id, name, description, ...)
|
|
exercise_media (id, exercise_id, type, url, title, description, ...)
|
|
```
|
|
|
|
### Training Planning
|
|
|
|
```sql
|
|
training_units (id, group_id, date, title, description, ...)
|
|
training_unit_exercises (training_unit_id, exercise_id, sort_order, ...)
|
|
```
|
|
|
|
---
|
|
|
|
## Geplante Änderungen (Migration 008)
|
|
|
|
### Hierarchische Katalog-Struktur
|
|
|
|
**Fachliche Anforderung (§8.1 Anforderungsdokument):**
|
|
```
|
|
Fokusbereich → Stil → Zielgruppe
|
|
```
|
|
|
|
**Neue Struktur:**
|
|
|
|
```sql
|
|
-- Level 1: Fokusbereiche (Root)
|
|
focus_areas (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(100) UNIQUE NOT NULL,
|
|
abbreviation VARCHAR(20),
|
|
description TEXT,
|
|
color VARCHAR(20),
|
|
icon VARCHAR(50),
|
|
sort_order INT,
|
|
status VARCHAR(50) DEFAULT 'active'
|
|
)
|
|
|
|
-- Level 2: Trainingsstile (untergeordnet zu Fokusbereich)
|
|
training_styles (
|
|
id SERIAL PRIMARY KEY,
|
|
focus_area_id INT REFERENCES focus_areas(id), -- Hierarchie-Link
|
|
name VARCHAR(100) NOT NULL,
|
|
abbreviation VARCHAR(20),
|
|
description TEXT,
|
|
parent_style_id INT REFERENCES training_styles(id), -- Optional: Sub-Stile
|
|
sort_order INT,
|
|
status VARCHAR(50) DEFAULT 'active'
|
|
)
|
|
|
|
-- Level 3: Zielgruppen (untergeordnet zu Stil)
|
|
target_groups (
|
|
id SERIAL PRIMARY KEY,
|
|
training_style_id INT REFERENCES training_styles(id), -- Hierarchie-Link
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
min_age INT,
|
|
max_age INT,
|
|
sort_order INT,
|
|
status VARCHAR(50) DEFAULT 'active'
|
|
)
|
|
```
|
|
|
|
### M:N Übungs-Zuordnungen
|
|
|
|
```sql
|
|
-- Übung → Fokusbereiche (M:N)
|
|
exercise_focus_areas (
|
|
id SERIAL PRIMARY KEY,
|
|
exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
|
|
focus_area_id INT REFERENCES focus_areas(id),
|
|
is_primary BOOLEAN DEFAULT false,
|
|
UNIQUE(exercise_id, focus_area_id)
|
|
)
|
|
|
|
-- Übung → Stile (M:N)
|
|
exercise_styles (
|
|
id SERIAL PRIMARY KEY,
|
|
exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
|
|
training_style_id INT REFERENCES training_styles(id),
|
|
is_primary BOOLEAN DEFAULT false,
|
|
UNIQUE(exercise_id, training_style_id)
|
|
)
|
|
|
|
-- Übung → Zielgruppen (M:N)
|
|
exercise_target_groups (
|
|
id SERIAL PRIMARY KEY,
|
|
exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
|
|
target_group_id INT REFERENCES target_groups(id),
|
|
is_primary BOOLEAN DEFAULT false,
|
|
UNIQUE(exercise_id, target_group_id)
|
|
)
|
|
|
|
-- Übung → Altersgruppen (M:N - separate Dimension)
|
|
exercise_age_groups (
|
|
id SERIAL PRIMARY KEY,
|
|
exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
|
|
age_group VARCHAR(50) NOT NULL, -- 'Minis', 'Kinder', 'Schüler', 'Teenager', 'Erwachsene'
|
|
UNIQUE(exercise_id, age_group)
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Kaskadier-Regeln (Geplant)
|
|
|
|
### Fokusbereich löschen
|
|
|
|
**Verhalten:** `RESTRICT` (Löschen verweigern wenn verwendet)
|
|
|
|
**Alternativen:**
|
|
1. **Umrouten:** Alle abhängigen Stile auf neuen Fokusbereich setzen
|
|
2. **Archivieren:** Status auf 'archived' setzen statt löschen
|
|
|
|
**Implementierung:**
|
|
```sql
|
|
-- Prüfung vor Löschen:
|
|
SELECT COUNT(*) FROM training_styles WHERE focus_area_id = :id
|
|
SELECT COUNT(*) FROM exercise_focus_areas WHERE focus_area_id = :id
|
|
|
|
-- Falls verwendet → HTTP 409 Conflict mit Hinweis
|
|
```
|
|
|
|
### Stil löschen
|
|
|
|
**Verhalten:** `RESTRICT` wenn Zielgruppen oder Übungen zugeordnet
|
|
|
|
**Umrouten-Workflow:**
|
|
1. Admin wählt Ziel-Stil
|
|
2. System aktualisiert:
|
|
- `target_groups.training_style_id`
|
|
- `exercise_styles.training_style_id`
|
|
3. Löschen erlaubt
|
|
|
|
### Zielgruppe löschen
|
|
|
|
**Verhalten:** `SET NULL` oder `CASCADE` in `exercise_target_groups`
|
|
|
|
---
|
|
|
|
## Indizes (Geplant)
|
|
|
|
```sql
|
|
-- Hierarchie-Lookups
|
|
CREATE INDEX idx_training_styles_focus_area ON training_styles(focus_area_id);
|
|
CREATE INDEX idx_target_groups_style ON target_groups(training_style_id);
|
|
|
|
-- M:N Joins
|
|
CREATE INDEX idx_exercise_focus_areas_exercise ON exercise_focus_areas(exercise_id);
|
|
CREATE INDEX idx_exercise_focus_areas_focus ON exercise_focus_areas(focus_area_id);
|
|
CREATE INDEX idx_exercise_styles_exercise ON exercise_styles(exercise_id);
|
|
CREATE INDEX idx_exercise_styles_style ON exercise_styles(training_style_id);
|
|
CREATE INDEX idx_exercise_target_groups_exercise ON exercise_target_groups(exercise_id);
|
|
CREATE INDEX idx_exercise_target_groups_target ON exercise_target_groups(target_group_id);
|
|
```
|
|
|
|
---
|
|
|
|
## Deprecation-Pfad (Migration 008)
|
|
|
|
**Legacy-Spalten in `exercises`:**
|
|
```sql
|
|
-- Werden in Migration 008 deprecated:
|
|
exercises.focus_area_id → Migriert zu exercise_focus_areas (is_primary=true)
|
|
exercises.training_style_id → Migriert zu exercise_styles (is_primary=true)
|
|
exercises.training_character_id → Bleibt vorerst (separate Dimension)
|
|
|
|
-- Spalten bleiben zur Rückwärtskompatibilität, aber:
|
|
-- 1. Daten migriert
|
|
-- 2. Neue Übungen nutzen M:N-Tabellen
|
|
-- 3. API liefert enriched data aus M:N
|
|
-- 4. Später: Spalten droppen nach Stabilisierung
|
|
```
|
|
|
|
---
|
|
|
|
## Nächste Schritte
|
|
|
|
- [ ] Migration 008 Entwurf erstellen
|
|
- [ ] Migrations-Strategie für Altdaten dokumentieren
|
|
- [ ] Admin-UI für Baum-Verwaltung planen
|
|
- [ ] API-Endpoints für hierarchische Navigation
|
|
- [ ] Cascade-Logik implementieren
|
|
- [ ] Tests für Löschen/Umrouten
|
|
|
|
---
|
|
|
|
**Letzte Aktualisierung:** 2026-04-23
|
|
**Verantwortlich:** Claude Code
|
|
**Review:** Pending
|