Finalize domain model: define event → tournament → class → division hierarchy, multi-tournament account structures, and event-wide cashbox aggregation logic. Add division types and update glossary tasks.
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
---
|
||||
type: DOMAIN_SPEC
|
||||
status: ACTIVE
|
||||
owner: Lead Architect
|
||||
last_update: 2026-04-02
|
||||
---
|
||||
|
||||
# Domänen‑Modell: Veranstaltung → Turnier → Bewerb → Abteilung
|
||||
|
||||
Ziel: Dieses Dokument fixiert das offizielle Kern‑Modell für die Event‑Struktur sowie Kassa/Konten. Es ist die Single Source of Truth für Backend‑Schema, Frontend‑ViewModels und Schnittstellen.
|
||||
|
||||
Quellen/Verweise:
|
||||
- Ubiquitous Language: `docs/03_Domain/01_Glossary/Ubiquitous_Language.md`
|
||||
- ÖTO/FEI Referenz: `docs/03_Domain/02_Reference/` (insb. Abteilungs‑Schwellenwerte)
|
||||
- ADR‑0021 Tenant‑Resolution (Event‑Isolation): `docs/01_Architecture/adr/0021-tenant-resolution-strategy-de.md`
|
||||
|
||||
## 1. Struktur und Kardinalitäten
|
||||
|
||||
Hierarchie und Identifikatoren (kanonisch):
|
||||
|
||||
```
|
||||
Veranstaltung (event_id)
|
||||
├─ Turnier (tournament_id) [1:N pro Veranstaltung]
|
||||
│ └─ Bewerb (class_id) [1:N pro Turnier]
|
||||
│ └─ Abteilung (division_id) [1:N pro Bewerb]
|
||||
└─ TeilnehmerKonto (account_id) [1:N pro Veranstaltung, referenziert Teilnehmer]
|
||||
└─ Veranstaltungs‑Kassa (event_cashbox_id = event_id) [1:1]
|
||||
```
|
||||
|
||||
Leitlinien:
|
||||
- Jede Veranstaltung ist ein eigener Tenant (Schema‑per‑Tenant gemäß ADR‑0021).
|
||||
- IDs sind innerhalb des Tenants eindeutig; globale Adressen entstehen durch `{event_id}/{local_id}`.
|
||||
|
||||
## 2. Entitäten und Aggregate
|
||||
|
||||
### 2.1 Veranstaltung
|
||||
- Schlüssel: `event_id` (Slug, z. B. `2026-moc-open`)
|
||||
- Aggregate‑Grenze: umfasst Metadaten der Veranstaltung, Kassa, TeilnehmerKonto‑Katalog.
|
||||
- Invarianten:
|
||||
- `status ∈ {draft, active, archived}`
|
||||
- Archivierte Veranstaltungen sind read‑only.
|
||||
|
||||
### 2.2 Turnier
|
||||
- Schlüssel: `tournament_id` (innerhalb Veranstaltung eindeutig)
|
||||
- Attribute (Auszug): Titel, Datum(e), Ort, Status.
|
||||
- Invarianten:
|
||||
- Ein Turnier gehört genau zu einer Veranstaltung.
|
||||
- Löschen nur erlaubt, wenn keine Nennungen/Ergebnisse bestätigt sind.
|
||||
|
||||
### 2.3 Bewerb
|
||||
- Schlüssel: `class_id`
|
||||
- Attribute: Disziplin, Klasse, Lizenzanforderungen, max Starter, Wertungsmodus.
|
||||
- Invarianten:
|
||||
- Ein Bewerb gehört genau zu einem Turnier.
|
||||
- Abteilungsbildung erfolgt gemäß Regelwerk/Schwellenwerten.
|
||||
|
||||
### 2.4 Abteilung
|
||||
- Schlüssel: `division_id`
|
||||
- Attribute: Lauf‑/Startzeit, Parcours/Bahn, Typ (siehe unten), Ergebnisstatus.
|
||||
- Invarianten:
|
||||
- Eine Abteilung gehört genau zu einem Bewerb.
|
||||
- Typen steuern UI, Zeitplan und Preisgeld-/Siegerehrungslogik.
|
||||
|
||||
### 2.5 TeilnehmerKonto (auf Veranstaltungsebene)
|
||||
- Zweck: Vereinheitlichte finanzielle Sicht je Teilnehmer über mehrere Turniere derselben Veranstaltung (Multi‑Turnier).
|
||||
- Schlüssel: `account_id`
|
||||
- Beziehungen:
|
||||
- `Teilnehmer` (z. B. Reiter, Verein, Team) 1:1 ↔ TeilnehmerKonto (pro Veranstaltung)
|
||||
- Buchungen entstehen aus Nennungen, Startgeldern, Gebühren, Gutschriften, Rückzahlungen turnierübergreifend.
|
||||
- Invarianten:
|
||||
- Ein Teilnehmer hat höchstens ein Konto pro Veranstaltung.
|
||||
- Saldo ist Summe aller bestätigten Buchungen innerhalb des Tenants.
|
||||
|
||||
### 2.6 Veranstaltungs‑Kassa (Turnier‑übergreifender Saldo)
|
||||
- Zweck: Aggregierte Kasse der gesamten Veranstaltung; spiegelt Einzahlungen/Auszahlungen und Summen über alle Turniere.
|
||||
- Schlüssel: `event_cashbox_id` = `event_id`
|
||||
- Komponenten:
|
||||
- Journal (Belege): Ein/Auszahlungen, Umbuchungen, Korrekturen.
|
||||
- Summen: aktueller Bestand, Reserven, offene Posten (aggregiert aus TeilnehmerKonten).
|
||||
- Invarianten:
|
||||
- Jede Buchung betrifft genau ein Gegenkonto (TeilnehmerKonto oder internes Konto).
|
||||
- Journal ist unveränderlich; Korrekturen erfolgen als Gegenbuchung.
|
||||
|
||||
## 3. Abteilungs‑Typen
|
||||
|
||||
Definiert als `enum DivisionType`:
|
||||
- `STANDARD`: Normale Abteilung mit regulärer Siegerehrung innerhalb des Bewerbs.
|
||||
- `SEPARATE_SIEGEREHRUNG`: Abteilung, deren Siegerehrung separat organisiert wird (z. B. zusammengelegt/zeitlich entkoppelt) — STATUS: vorläufig, Detailregeln folgen durch 📜 Rulebook Expert.
|
||||
- `ORGANISATORISCH`: Rein organisatorische Abteilung (z. B. Aufteilung aus Zeit/Platz‑Gründen), ohne eigenständige sportliche Wertung/Preisgeldlogik.
|
||||
|
||||
Hinweis: Die genaue Ausgestaltung von `SEPARATE_SIEGEREHRUNG` (Preisgeld‑Aggregation, Ranking‑Anzeige, Protokoll) wird im Rulebook‑Dokument ergänzt und kann weitere Felder/Beziehungen erfordern (z. B. Verweis auf „gemeinsame Siegerehrung für Bewerbe X/Y“).
|
||||
|
||||
## 4. Datenmodell‑Skizze (relationale Sicht je Tenant)
|
||||
|
||||
```sql
|
||||
-- Veranstaltung (im Tenant‑Schema)
|
||||
CREATE TABLE event (
|
||||
event_id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ('draft','active','archived'))
|
||||
);
|
||||
|
||||
CREATE TABLE tournament (
|
||||
tournament_id TEXT PRIMARY KEY,
|
||||
event_id TEXT NOT NULL REFERENCES event(event_id),
|
||||
title TEXT NOT NULL,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
status TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE class (
|
||||
class_id TEXT PRIMARY KEY,
|
||||
tournament_id TEXT NOT NULL REFERENCES tournament(tournament_id),
|
||||
discipline TEXT NOT NULL,
|
||||
level TEXT NOT NULL,
|
||||
max_starters INT,
|
||||
scoring_mode TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE division (
|
||||
division_id TEXT PRIMARY KEY,
|
||||
class_id TEXT NOT NULL REFERENCES class(class_id),
|
||||
type TEXT NOT NULL CHECK (type IN ('STANDARD','SEPARATE_SIEGEREHRUNG','ORGANISATORISCH')),
|
||||
scheduled_at TIMESTAMP,
|
||||
status TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- TeilnehmerKonto (veranstaltungsweit)
|
||||
CREATE TABLE participant_account (
|
||||
account_id TEXT PRIMARY KEY,
|
||||
event_id TEXT NOT NULL REFERENCES event(event_id),
|
||||
participant_ref TEXT NOT NULL, -- verweist auf Teilnehmer‑Stammdatensatz im Tenant
|
||||
UNIQUE(event_id, participant_ref)
|
||||
);
|
||||
|
||||
CREATE TABLE participant_ledger_entry (
|
||||
entry_id TEXT PRIMARY KEY,
|
||||
account_id TEXT NOT NULL REFERENCES participant_account(account_id),
|
||||
booking_ts TIMESTAMP NOT NULL,
|
||||
amount_cents BIGINT NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'EUR',
|
||||
source TEXT NOT NULL, -- z. B. Nennung, Startgeld, Rückzahlung
|
||||
tournament_id TEXT NULL REFERENCES tournament(tournament_id)
|
||||
);
|
||||
|
||||
-- Veranstaltungs‑Kassa
|
||||
CREATE TABLE event_cashbox (
|
||||
event_cashbox_id TEXT PRIMARY KEY REFERENCES event(event_id),
|
||||
created_at TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE cashbox_journal (
|
||||
journal_id TEXT PRIMARY KEY,
|
||||
event_cashbox_id TEXT NOT NULL REFERENCES event_cashbox(event_cashbox_id),
|
||||
booking_ts TIMESTAMP NOT NULL,
|
||||
amount_cents BIGINT NOT NULL,
|
||||
direction TEXT NOT NULL CHECK (direction IN ('IN','OUT')),
|
||||
counterparty TEXT NOT NULL, -- account_id oder internes Konto
|
||||
memo TEXT
|
||||
);
|
||||
```
|
||||
|
||||
## 5. Invarianten und Geschäftsregeln (Auszug)
|
||||
- Abteilungs‑Typ `ORGANISATORISCH` darf keine eigenständige Preisgeldlogik auslösen.
|
||||
- `SEPARATE_SIEGEREHRUNG` kann Ergebnisse bündeln/verschieben; Detailregeln werden im Rulebook spezifiziert. Bis dahin bleiben API‑Felder stabil, Verhalten konservativ (keine automatische Zusammenlegung ohne explizite Verknüpfung).
|
||||
- TeilnehmerKonto‑Saldo = Summe aller bestätigten `participant_ledger_entry.amount_cents`.
|
||||
- Event‑Kassa‑Bestand = Summe `IN` − Summe `OUT`; regelmäßige Abstimmung mit Summe aller Teilnehmer‑Offenen Posten.
|
||||
|
||||
## 6. API/DTO Richtlinien (High‑Level)
|
||||
- Alle API‑Ressourcen werden unterhalb des Tenants adressiert (Header `X-Event-Id`).
|
||||
- DTOs tragen stabile `*_id` Felder entsprechend diesem Modell; Referenzen sind per ID, keine eingebetteten Aggregate außer Read‑Views.
|
||||
- Enum `DivisionType` wird exakt wie oben benannt; neue Typen erfordern Versionserhöhung des Schemas.
|
||||
|
||||
## 7. ToDos und Folgearbeiten
|
||||
- 📜 Rulebook Expert: Detail‑Spezifikation `SEPARATE_SIEGEREHRUNG` (Preisgeld, Ranking, UI‑Hinweise) ergänzen.
|
||||
- 🧹 Curator: `Ubiquitous_Language.md` um obige Begriffe/Definitionen erweitern.
|
||||
- 👷 Backend: Schema‑Migrationen pro Tenant gemäß obiger Tabellen; Repositories/Services entsprechend zuschneiden.
|
||||
- 🎨 Frontend: ViewModels/Stores entlang dieser Struktur aktualisieren (Navigation: Veranstaltung → Turnier → Bewerb → Abteilung).
|
||||
Reference in New Issue
Block a user