shinkan-jinkendo/.claude/docs/technical/DATABASE_SCHEMA.md
Lars 63b1c09975
Some checks failed
Deploy Development / deploy (push) Successful in 34s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 5s
Test Suite / playwright-tests (push) Has been cancelled
feat: Migration 008 - M:N Exercise Relations + Hierarchical Catalogs
BREAKING CHANGE: Datenmodell-Umstellung von 1:1 auf M:N Beziehungen

Migration 008:
- Zielgruppen-Tabelle (target_groups) mit training_style_id Hierarchie
- M:N Zuordnungstabellen: exercise_focus_areas, exercise_styles, exercise_target_groups
- Altersgruppen-Dimension (exercise_age_groups) mit CHECK constraint
- Hierarchische Struktur: training_styles.focus_area_id → focus_areas
- Daten-Migration: Bestehende 1:1 Beziehungen zu M:N mit is_primary=true
- Seed-Daten: Beispiel-Zielgruppen für Shotokan

Architektur:
- Smart Cascade-Logik (RESTRICT, Rerouting, Move) vorbereitet
- Legacy-Spalten (focus_area_id, training_style_id) bleiben zur Rückwärtskompatibilität
- Primary/Secondary Assignments via is_primary Flag

Dokumentation:
- .claude/docs/technical/DATABASE_SCHEMA.md (kontinuierlich gepflegt)
- .claude/docs/functional/DOMAIN_MODEL.md (fachliche Anforderungen)
- Migrations-Historie aktualisiert

version: 0.3.0 (backend + frontend)
modules: exercises 0.3.0, catalogs 1.1.0
DB_SCHEMA_VERSION: 20260423

Konzept: shinkan_anforderungsdokument_entwurf.md (§8.1, §8.3, §10.7)
2026-04-23 08:46:56 +02:00

7.2 KiB

Shinkan Jinkendo - Datenbank-Schema (Technisch)

Version: 0.2.0
Stand: 2026-04-23
Aktuell deployed: Migration 007 (idempotent)


Ü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 Erstellt, Test ausstehend

Kern-Entitäten (Stand Migration 007)

Auth & Profile

profiles (id, name, email, password_hash, role, created_at)
sessions (id, profile_id, token, created_at, expires_at)

Organisation

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!

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)

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

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:

-- 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

-- Ü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:

-- 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)

-- 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:

-- 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