docs: massive restructuring of documentation, development guides and agent playbooks
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).
|
||||
@@ -0,0 +1,241 @@
|
||||
-- Database Schema Draft for Meldestelle (Offline-First)
|
||||
-- Dialect: SQLite (compatible with SQLDelight)
|
||||
-- Status: Draft / Proposal
|
||||
-- Based on: OEPS Legacy Spec V2.4 & Domain Analysis
|
||||
|
||||
-- ==================================================================
|
||||
-- 1. CORE INFRASTRUCTURE (Sync & Audit)
|
||||
-- ==================================================================
|
||||
-- Every table should ideally have these fields, but for brevity
|
||||
-- they are implied or added where critical.
|
||||
-- id: TEXT NOT NULL PRIMARY KEY (UUID)
|
||||
-- created_at: INTEGER NOT NULL (Epoch Millis)
|
||||
-- updated_at: INTEGER NOT NULL (Epoch Millis)
|
||||
-- version: INTEGER NOT NULL (Optimistic Locking / Sync Counter)
|
||||
-- is_deleted: INTEGER NOT NULL DEFAULT 0 (Soft Delete)
|
||||
|
||||
-- ==================================================================
|
||||
-- 2. MASTER DATA (Stammdaten)
|
||||
-- ==================================================================
|
||||
|
||||
-- Akteure: Personen und Organisationen
|
||||
-- Covers: Reiter, Richter, Besitzer, Vereine
|
||||
CREATE TABLE actor (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
type TEXT NOT NULL, -- 'PERSON', 'ORGANIZATION'
|
||||
|
||||
-- Display Data
|
||||
first_name TEXT, -- NULL for Organizations
|
||||
last_name TEXT NOT NULL, -- Name or Org-Name
|
||||
|
||||
-- OEPS Specifics (Legacy Spec)
|
||||
oeps_id TEXT, -- 'Satznummer' (6 digits for Person, 4 for Club)
|
||||
oeps_category TEXT, -- 'Verein', 'Reiter', 'Richter'
|
||||
|
||||
-- Licenses & Status
|
||||
license_code TEXT, -- e.g. 'R1', 'RD3'
|
||||
has_start_card INTEGER NOT NULL DEFAULT 0, -- Boolean: Paid annual fee?
|
||||
is_locked INTEGER NOT NULL DEFAULT 0, -- Boolean: Sperrliste?
|
||||
|
||||
-- Contact & Meta
|
||||
nationality TEXT NOT NULL DEFAULT 'AUT', -- ISO 3-Letter
|
||||
contact_json TEXT, -- Address, Phone, Email
|
||||
|
||||
-- Sync Meta
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
version INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX idx_actor_oeps_id ON actor(oeps_id);
|
||||
CREATE INDEX idx_actor_name ON actor(last_name, first_name);
|
||||
|
||||
|
||||
-- Pferde
|
||||
CREATE TABLE horse (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
|
||||
-- Identification
|
||||
oeps_id TEXT, -- 'Satznummer' (10 digits) - CRITICAL for Export
|
||||
head_number_permanent TEXT, -- 'Kopfnummer' (e.g. A123)
|
||||
life_number TEXT, -- 'Lebensnummer' (Zucht)
|
||||
fei_id TEXT,
|
||||
|
||||
-- Details
|
||||
birth_year INTEGER,
|
||||
gender TEXT, -- 'M', 'W', 'G' (Gelding/Wallach)
|
||||
color TEXT,
|
||||
sire_name TEXT, -- Vater (Denormalized for search)
|
||||
dam_name TEXT, -- Mutter
|
||||
|
||||
-- Owner Link
|
||||
owner_id TEXT, -- FK to actor.id
|
||||
|
||||
-- Status
|
||||
is_locked INTEGER NOT NULL DEFAULT 0, -- Sperrliste
|
||||
|
||||
-- Sync Meta
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
version INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX idx_horse_oeps_id ON horse(oeps_id);
|
||||
CREATE INDEX idx_horse_head_num ON horse(head_number_permanent);
|
||||
CREATE INDEX idx_horse_name ON horse(name);
|
||||
|
||||
-- ==================================================================
|
||||
-- 3. EVENT STRUCTURE
|
||||
-- ==================================================================
|
||||
|
||||
CREATE TABLE event (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
start_date INTEGER NOT NULL, -- Epoch Day
|
||||
end_date INTEGER NOT NULL,
|
||||
location TEXT,
|
||||
organizer_id TEXT NOT NULL, -- FK to actor.id
|
||||
|
||||
status TEXT NOT NULL DEFAULT 'PLANNING' -- PLANNING, ACTIVE, ARCHIVED
|
||||
);
|
||||
|
||||
CREATE TABLE tournament (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
event_id TEXT NOT NULL REFERENCES event(id),
|
||||
|
||||
-- OEPS Spec
|
||||
oeps_number TEXT NOT NULL, -- 5 digits (e.g. 21001)
|
||||
category TEXT, -- e.g. 'CSN-A'
|
||||
ruleset TEXT NOT NULL DEFAULT 'OETO', -- 'OETO', 'FEI'
|
||||
|
||||
-- Sync Meta
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- Bewerbe (Competitions)
|
||||
-- Note: If a competition is split into 2 departments (Abteilungen),
|
||||
-- we create 2 rows here to match the OEPS 'B-Satz' logic.
|
||||
CREATE TABLE competition (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
tournament_id TEXT NOT NULL REFERENCES tournament(id),
|
||||
|
||||
-- Identification
|
||||
code_internal TEXT NOT NULL, -- '01', '02' (2 digits)
|
||||
code_official TEXT, -- '001' (3 digits, optional)
|
||||
division_id INTEGER NOT NULL DEFAULT 0, -- 'Abteilung' (0=None, 1=1st, 2=2nd)
|
||||
|
||||
-- Description
|
||||
title TEXT NOT NULL,
|
||||
category TEXT, -- e.g. 'LM', 'S*'
|
||||
discipline TEXT NOT NULL, -- 'D', 'S', 'C' (Dressage, Jumping, Combined)
|
||||
|
||||
-- Rules & Scoring
|
||||
scoring_method TEXT NOT NULL, -- 'A0', 'C', 'DRESSAGE_PERCENT'
|
||||
start_fee INTEGER NOT NULL DEFAULT 0, -- In Cents
|
||||
|
||||
-- State
|
||||
status TEXT NOT NULL DEFAULT 'OPEN', -- OPEN, CLOSED_FOR_ENTRIES, RUNNING, FINISHED, SIGNED_OFF
|
||||
|
||||
-- Sync Meta
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_comp_tournament ON competition(tournament_id);
|
||||
|
||||
-- ==================================================================
|
||||
-- 4. SPORT & PROCESS
|
||||
-- ==================================================================
|
||||
|
||||
-- Nennungen (Entries)
|
||||
-- Represents the intent to start.
|
||||
CREATE TABLE entry (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
competition_id TEXT NOT NULL REFERENCES competition(id),
|
||||
|
||||
-- The Pair
|
||||
horse_id TEXT NOT NULL REFERENCES horse(id),
|
||||
rider_id TEXT NOT NULL REFERENCES actor(id),
|
||||
|
||||
-- Financials
|
||||
responsible_person_id TEXT REFERENCES actor(id), -- Who pays?
|
||||
fee_agreed INTEGER NOT NULL, -- In Cents (Snapshot of price at entry time)
|
||||
payment_status TEXT NOT NULL DEFAULT 'PENDING', -- PENDING, PAID, WAIVED
|
||||
|
||||
-- Validation Override (The "Human Factor")
|
||||
validation_status TEXT NOT NULL DEFAULT 'OK', -- OK, WARNING, BLOCKED
|
||||
override_comment TEXT, -- Why was this allowed despite warning?
|
||||
|
||||
-- Sync Meta
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_entry_comp ON entry(competition_id);
|
||||
CREATE INDEX idx_entry_rider ON entry(rider_id);
|
||||
|
||||
-- Startliste (Start Order)
|
||||
-- Subset of entries that actually start.
|
||||
CREATE TABLE start_list_entry (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
entry_id TEXT NOT NULL REFERENCES entry(id),
|
||||
|
||||
-- Ordering
|
||||
start_order INTEGER, -- 1, 2, 3...
|
||||
start_time_planned INTEGER, -- Epoch Millis (optional)
|
||||
|
||||
-- Tournament Specifics
|
||||
head_number_event TEXT, -- Startnummer am Turnier (kann von A123 abweichen)
|
||||
|
||||
-- Status
|
||||
status TEXT NOT NULL DEFAULT 'READY', -- READY, STARTED, DNS (Did Not Start)
|
||||
|
||||
UNIQUE(entry_id)
|
||||
);
|
||||
|
||||
-- Ergebnisse (Results)
|
||||
CREATE TABLE result (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
start_list_entry_id TEXT NOT NULL REFERENCES start_list_entry(id),
|
||||
|
||||
-- The Outcome
|
||||
rank INTEGER, -- 1, 2, 3... (NULL if eliminated)
|
||||
|
||||
-- Scoring Details (Polymorphic based on Competition Type)
|
||||
points_jump_faults DECIMAL(5,2), -- Springfehler
|
||||
time_taken_ms INTEGER, -- Zeit in Millisekunden
|
||||
score_dressage_percent DECIMAL(5,3), -- 72.500
|
||||
score_dressage_total DECIMAL(6,2), -- Summe Punkte
|
||||
|
||||
-- Status Flags
|
||||
classification TEXT NOT NULL DEFAULT 'OK', -- OK, EL (Elim), RET (Retired), DIS (Disq)
|
||||
|
||||
-- Detailed Marks (JSON)
|
||||
-- e.g. { "judge_C": 7.5, "judge_H": 7.2, "obstacles": [...] }
|
||||
details_json TEXT,
|
||||
|
||||
-- Money
|
||||
prize_money INTEGER DEFAULT 0, -- In Cents
|
||||
|
||||
-- Audit
|
||||
updated_by_user_id TEXT,
|
||||
updated_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_result_starter ON result(start_list_entry_id);
|
||||
|
||||
-- ==================================================================
|
||||
-- 5. AUDIT LOG (NFR-07)
|
||||
-- ==================================================================
|
||||
|
||||
CREATE TABLE audit_log (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
entity_type TEXT NOT NULL, -- 'RESULT', 'ENTRY'
|
||||
entity_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL, -- 'CREATE', 'UPDATE', 'DELETE'
|
||||
|
||||
user_id TEXT,
|
||||
timestamp INTEGER NOT NULL,
|
||||
|
||||
changes_json TEXT -- { "score_old": 7.0, "score_new": 7.5 }
|
||||
);
|
||||
@@ -0,0 +1,177 @@
|
||||
# Turnier- & Bewerbsstruktur Diagramm
|
||||
|
||||
Dieses Diagramm zeigt die strukturelle Hierarchie und die Beziehungen zwischen Event, Turnier und Bewerb basierend auf
|
||||
dem ÖTO-Regelwerk und den Anforderungen der OEPS-Schnittstelle.
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
%% Entities
|
||||
EVENT {
|
||||
string id PK "UUID"
|
||||
string name "z.B. Apropos Pferd 2026"
|
||||
date start_date
|
||||
date end_date
|
||||
string location
|
||||
string organizer_id FK "Veranstalter (Akteur)"
|
||||
string status "PLANNING, ACTIVE, ARCHIVED"
|
||||
}
|
||||
|
||||
TOURNAMENT {
|
||||
string id PK "UUID"
|
||||
string event_id FK
|
||||
string oeps_number "z.B. 21001 (A-Satz)"
|
||||
string category "z.B. CSN-A, CDN-B, CDN-C-NEU"
|
||||
string ruleset "OETO oder FEI"
|
||||
}
|
||||
|
||||
COMPETITION {
|
||||
string id PK "UUID"
|
||||
string tournament_id FK
|
||||
string code_internal "2-stellig (B-Satz Pos. 2)"
|
||||
string code_official "3-stellig (B-Satz Pos. 61)"
|
||||
int division_id "Abteilung (0=keine, 1=Abt. 1...)"
|
||||
string title "z.B. Standardspringprüfung"
|
||||
string discipline "S (Springen), D (Dressur), C"
|
||||
string category "Klasse z.B. A, L, M, S*, lizenzfrei"
|
||||
string scoring_method "Richtverfahren (z.B. A0, A2, AM5, Stil)"
|
||||
int start_fee "in Cent"
|
||||
string status "OPEN, RUNNING, FINISHED"
|
||||
int planned_duration_per_starter_seconds "Kalkulierte Reitzeit pro Starter"
|
||||
}
|
||||
|
||||
OFFICIALS {
|
||||
string id PK "UUID"
|
||||
string tournament_id FK "oder competition_id"
|
||||
string actor_id FK "Richter / Parcoursbauer"
|
||||
string role "Hauptrichter, Richter, TD, Parcoursbauer"
|
||||
}
|
||||
|
||||
ACTOR {
|
||||
string id PK "UUID"
|
||||
string oeps_id "z.B. 123456"
|
||||
string type "PERSON, ORGANIZATION"
|
||||
string name "Vollständiger Name"
|
||||
string license_code "z.B. R1"
|
||||
}
|
||||
|
||||
QUALIFICATION {
|
||||
string id PK "UUID"
|
||||
string actor_id FK
|
||||
string discipline "S, D, V"
|
||||
string level "z.B. L, M, S"
|
||||
string role "RICHTER, PARCOURSBAUER"
|
||||
}
|
||||
|
||||
ENTRY {
|
||||
string id PK "UUID"
|
||||
string competition_id FK
|
||||
string horse_id FK
|
||||
string rider_id FK
|
||||
string status "ACTIVE, WITHDRAWN"
|
||||
string preferred_start_position "Optional: vorne, hinten, zu"
|
||||
}
|
||||
|
||||
START_LIST_ENTRY {
|
||||
string id PK "UUID"
|
||||
string entry_id FK
|
||||
int start_order "Nummer in der Startreihenfolge"
|
||||
string status "READY, IN_PROGRESS, FINISHED, DNS"
|
||||
}
|
||||
|
||||
RESULT {
|
||||
string id PK "UUID"
|
||||
string start_list_entry_id FK
|
||||
int rank "Platzierung (kann manuell überschrieben werden)"
|
||||
string status "OK, EL, RET, DNS"
|
||||
float points "Fehlerpunkte / Wertnote"
|
||||
float time_seconds "Benötigte Zeit"
|
||||
int prize_money "Ausbezahltes Geld in Cent"
|
||||
}
|
||||
|
||||
BILLING_ACCOUNT {
|
||||
string id PK "UUID"
|
||||
string event_id FK
|
||||
string payer_id FK "Akteur (Zahler)"
|
||||
int current_balance "in Cent"
|
||||
}
|
||||
|
||||
TRANSACTION {
|
||||
string id PK "UUID"
|
||||
string account_id FK
|
||||
string entry_id FK "Optionaler Bezug zur Nennung"
|
||||
string type "NENNGELD, STARTGELD, TAUSCHGEBÜHR, NACHNENNUNG"
|
||||
int amount "in Cent"
|
||||
}
|
||||
|
||||
%% Relationships
|
||||
EVENT ||--o{ TOURNAMENT: "beinhaltet"
|
||||
TOURNAMENT ||--o{ COMPETITION: "besteht aus (B-Satz)"
|
||||
TOURNAMENT ||--o{ OFFICIALS: "hat Funktionäre (C-Satz)"
|
||||
COMPETITION ||--o{ OFFICIALS: "wird gerichtet von"
|
||||
OFFICIALS }o--|| ACTOR: "ist ein"
|
||||
ACTOR ||--o{ QUALIFICATION: "hat Berechtigungen"
|
||||
COMPETITION ||--o{ ENTRY: "hat Nennungen"
|
||||
ENTRY ||--o| START_LIST_ENTRY: "wird zu Starter"
|
||||
START_LIST_ENTRY ||--o| RESULT: "hat Ergebnis"
|
||||
EVENT ||--o{ BILLING_ACCOUNT: "verwaltet Kassa für"
|
||||
BILLING_ACCOUNT ||--o{ TRANSACTION: "verbucht"
|
||||
```
|
||||
|
||||
## Erläuterung zum Modell
|
||||
|
||||
### 1. `EVENT` (Veranstaltung)
|
||||
|
||||
Das **Event** ist der übergeordnete organisatorische Rahmen (z.B. "Pferdefestival 2026"). Es hat ein Datum, einen Ort
|
||||
und einen Veranstalter. Es existiert unabhängig von den strikten OEPS-Regularien und ist primär für die administrative
|
||||
Verwaltung (Rechnungsstellung, Anlagenplanung) wichtig.
|
||||
|
||||
### 2. `TOURNAMENT` (Turnier)
|
||||
Ein Event kann mehrere **Turniere** beinhalten (z.B. ein nationales CSN-B und gleichzeitig ein internationales CSI2*).
|
||||
Das Turnier ist die Instanz, die strikt an das Regelwerk gebunden ist:
|
||||
* Es korrespondiert 1:1 mit dem **A-Satz** des OEPS-Pflichtenhefts.
|
||||
* Es hat eine eindeutige 5-stellige `oeps_number`.
|
||||
* Es legt fest, nach welchem Regelwerk (ÖTO vs. FEI) geritten und ausgewertet wird. In der ersten Phase konzentrieren
|
||||
wir uns auf **C-NEU** und **C**.
|
||||
|
||||
### 3. `COMPETITION` (Bewerb / Prüfung)
|
||||
|
||||
Die kleinste sportliche Einheit und das Herzstück der Ausschreibung. Wir fokussieren uns initial auf **Dressur (D)** und
|
||||
**Springen (S)**.
|
||||
Hier finden wir die direkte Verbindung zum **B-Satz** der Legacy-Spezifikation:
|
||||
|
||||
* **Abteilungen (`division_id`):** Laut OEPS-Schnittstelle wird jede Abteilung (z.B. R1-Reiter getrennt von R2-Reitern)
|
||||
datentechnisch fast wie ein eigener Bewerb behandelt. In unserer Datenbank repräsentieren wir jede Abteilung als
|
||||
eigene Zeile in der Tabelle `competition` (oder verknüpfen sie intelligent), da jede Abteilung ihre eigene
|
||||
Ergebnisliste und ihr eigenes Preisgeld hat.
|
||||
* **Nummerierung:** Intern 2-stellig (`code_internal`), für Turniere ab 100 Bewerben offiziell 3-stellig (
|
||||
`code_official` an Stelle 61).
|
||||
* **Richtverfahren:** Entscheidet darüber, wie Fehler und Zeit (Springen) oder Wertnoten (Dressur) in ein Ranking
|
||||
übersetzt werden.
|
||||
* **Zeitplanung:** `planned_duration_per_starter_seconds` ist für die Kalkulation der Startzeiten elementar.
|
||||
|
||||
### 4. `OFFICIALS` & `QUALIFICATION` (Der C-Satz & ZNS-Import)
|
||||
|
||||
Dies entspricht dem **C-Satz**. Richter und Parcoursbauer müssen einem Turnier oder spezifisch einem Bewerb zugewiesen
|
||||
werden.
|
||||
|
||||
* Neu: Die Entität `QUALIFICATION` bildet die importierten Lizenzstufen aus der ZNS-Datei `RICHT01.DAT` ab. Das Backend
|
||||
gleicht diese Berechtigungen bei der Zuweisung gegen die Kategorie der `COMPETITION` ab.
|
||||
|
||||
### 5. `ENTRY`, `START_LIST_ENTRY` & `RESULT` (Nennung bis Ergebnis)
|
||||
|
||||
* **`ENTRY` (Nennung):** Die Nennung verknüpft ein Pferd und einen Reiter mit einem bestimmten Bewerb. Hier werden auch
|
||||
**Startwünsche** (vorne/hinten) erfasst, die für den Telefon-Workflow kritisch sind.
|
||||
* **`START_LIST_ENTRY` (Startliste):** Generiert aus den aktiven Nennungen. Definiert die exakte Startreihenfolge.
|
||||
* **`RESULT` (Ergebnis):** Hält die im "Richterturm-Workflow" erfassten Rohdaten (Punkte/Zeit) und die errechnete
|
||||
Platzierung. Die Platzierung kann bei Bedarf manuell vom Veranstalter überschrieben werden (z.B. wenn mehr Platziert
|
||||
werden sollen, als die ÖTO vorschlägt).
|
||||
|
||||
### 6. Billing Context (`BILLING_ACCOUNT` & `TRANSACTION`)
|
||||
|
||||
Die Abrechnung (Kassa) wird als separater Bereich an das `EVENT` gehängt.
|
||||
|
||||
* Es wird ein `BILLING_ACCOUNT` pro "Zahler" (meist ein `ACTOR`) geführt.
|
||||
* Jede Gebühr (Nenngeld, Startgeld, Nachnenngebühr) wird als atomare `TRANSACTION` verbucht.
|
||||
* Wenn ein **Nennungstausch** stattfindet, bleibt das Nenngeld als positive Transaktion auf dem Account erhalten,
|
||||
während für die neue Nennung lediglich eine neue Startgeld- oder Tauschgebühr-Transaktion erzeugt wird. Das ermöglicht
|
||||
maximale Flexibilität.
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Lead Architect
|
||||
---
|
||||
# 01 - Core Domain Entities
|
||||
|
||||
Dieses Dokument definiert die zentralen fachlichen Entitäten (Kern-Entitäten) des "Meldestelle"-Projekts. Diese Entitäten bilden das Fundament des Datenmodells und der gesamten Anwendungslogik.
|
||||
|
||||
> **Hinweis:** Dieses Modell wurde basierend auf der Analyse des OEPS-Pflichtenhefts 2021 V2.4 verfeinert.
|
||||
|
||||
## Die 6 Kern-Entitäten
|
||||
|
||||
1. **Event**: Der organisatorische Rahmen.
|
||||
2. **Turnier**: Die administrative, regelbasierte Einheit.
|
||||
3. **Bewerb**: Die einzelne sportliche Prüfung.
|
||||
4. **Wertungsserie**: Der übergeordnete Cup oder die Meisterschaft.
|
||||
5. **Akteur**: Personen und Organisationen.
|
||||
6. **Pferd**: Die Pferde als eigenständige Entität.
|
||||
|
||||
---
|
||||
|
||||
### 1. Entität: `Veranstaltung` (Event)
|
||||
|
||||
**Zweck:** Der übergeordnete organisatorische Container für eine Veranstaltung an einem bestimmten Ort und zu einer bestimmten Zeit. Eine Veranstaltung kann ein oder mehrere Turniere umfassen.
|
||||
|
||||
**Beispiele:** "Apropos Pferd 2026", "Vereinsturnier Reitclub XY".
|
||||
|
||||
**Attribute:**
|
||||
* `Veranstaltung-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Name`: Offizieller Name der Veranstaltung.
|
||||
* `Veranstaltungsort`: Adresse und Name der Anlage.
|
||||
* `Datum_Von`: Startdatum des Events.
|
||||
* `Datum_Bis`: Enddatum des Events.
|
||||
* `Veranstalter_ID` (FK): Verweis auf den `Akteur`, der die Veranstaltung ausrichtet.
|
||||
* `Sparten`: Liste der angebotenen Sparten.
|
||||
* `Austragungsplätze`: Liste der Austragungsplätze (`austragungsplaetze`).
|
||||
* `Artikel-Preisliste`: Liste der Zusatzartikel inkl. Preise (`artikelPreisliste`).
|
||||
* `Status`: Grober Zustand des Events (z.B. `In Planung`, `Laufend`, `Abgeschlossen`).
|
||||
|
||||
---
|
||||
|
||||
### 2. Entität: `Turnier`
|
||||
|
||||
**Zweck:** Definiert eine administrative Einheit innerhalb eines Events, die unter einem einheitlichen Regelwerk stattfindet. Hier werden Nennungen, Starter- und Ergebnislisten verwaltet.
|
||||
|
||||
**Beispiele:** "CSN-A im Rahmen der Apropos Pferd", "CSI2* im Rahmen der Apropos Pferd".
|
||||
|
||||
**Attribute:**
|
||||
* `Turnier-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Veranstaltung_ID` (FK): Verweis auf die übergeordnete `Veranstaltung`.
|
||||
* `Turniernummer_OEPS`: 5-stellige Nummer (z.B. `21001`) für den Datenaustausch.
|
||||
* `Reglement`: Entscheidende Weiche für die Anwendungslogik (Enum: `OETO`, `FEI`).
|
||||
* `Kategorie`: Offizielle Turnierkategorie (z.B. "CSN-A", "CSI2*", "CDI-W").
|
||||
* `Sparte`: Sparte des Turniers (z.B. `Springen`, `Dressur`).
|
||||
* `Turnierbeauftragter_ID` (FK): Referenz auf den Turnierbeauftragten (TB).
|
||||
* `Ausschreibung_Text`: Der vollständige Text der Ausschreibung.
|
||||
* `Nennschluss`: Datum und Uhrzeit.
|
||||
* `NachnenngebuehrVerlangt`: Flag, ob Nachnenngebühr erhoben wird.
|
||||
* `NenntauschboerseAktiv`: Flag, ob Nenntauschbörse aktiv ist.
|
||||
* `Status`: Detaillierter Zustand des Turniers (z.B. `Genehmigt`, `Nennschluss`, `Ergebnisse final`).
|
||||
|
||||
---
|
||||
|
||||
### 3. Entität: `Bewerb`
|
||||
|
||||
**Zweck:** Die einzelne sportliche Prüfung innerhalb eines Turniers. Ein Bewerb ist die kleinste Einheit, für die eine Nennung möglich ist und eine Ergebnisliste erstellt wird.
|
||||
|
||||
**Beispiele:** "Standardspringprüfung Kl. L", "Dressurprüfung Kl. M - Aufgabe M5".
|
||||
|
||||
**Attribute:**
|
||||
* `Bewerb-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Turnier_ID` (FK): Verweis auf das zugehörige `Turnier`.
|
||||
* `Nummer_Intern`: 2-stellige Nummer (z.B. `05`).
|
||||
* `Nummer_Offiziell`: 3-stellige Nummer (z.B. `005`) für Turniere > 99 Bewerbe.
|
||||
* `Abteilung`: Kennzeichen für Unterteilungen (z.B. `1`, `2`). Default `0`.
|
||||
* `Titel`: Der offizielle Titel des Bewerbs.
|
||||
* `Startgeld`: Das für diesen Bewerb zu entrichtende Startgeld (in EUR).
|
||||
* `Startberechtigung_Text`: Textuelle Beschreibung der Teilnahmevoraussetzungen.
|
||||
* `Besondere_Bestimmungen`: Spezielle Regeln nur für diesen Bewerb.
|
||||
|
||||
---
|
||||
|
||||
### 4. Entität: `Wertungsserie`
|
||||
|
||||
**Zweck:** Definiert eine übergeordnete Wertung (Cup, Meisterschaft), die Ergebnisse aus spezifischen Bewerben über mehrere Turniere hinweg sammelt und nach einem eigenen Regelwerk auswertet.
|
||||
|
||||
**Beispiele:** "Casino Grand Prix 2026", "OÖ Landesmeisterschaft Dressur Allgemeine Klasse".
|
||||
|
||||
**Attribute:**
|
||||
* `Serie-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Name`: Offizieller Name der Serie.
|
||||
* `Saison`: Das Jahr, in dem die Serie stattfindet.
|
||||
* `Reglement_Text`: Die spezifischen Regeln für die Wertung (Punktesystem, etc.).
|
||||
* `Teilnahmeberechtigung_Text`: Regeln, wer an der Serie teilnehmen darf.
|
||||
* `Qualifikationsbewerbe`: Eine Liste von Verweisen auf die `Bewerb-IDs`, deren Ergebnisse für diese Serie gewertet werden.
|
||||
|
||||
---
|
||||
|
||||
### 5. Entität: `Akteur`
|
||||
|
||||
**Zweck:** Zentrale, widerspruchsfreie Verwaltung aller beteiligten Personen und Organisationen.
|
||||
|
||||
**Beispiele:** Ein Reiter, ein Pferdebesitzer, ein Züchter, ein Richter, ein Reitverein.
|
||||
|
||||
**Attribute:**
|
||||
* `Akteur-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Typ`: `PERSON` oder `ORGANISATION`.
|
||||
* `Name`: Vollständiger Name der Person oder Organisation.
|
||||
* `Kontakt`: Adress- und Kontaktdaten.
|
||||
* `Rollen`: Liste der Rollen (z.B. `REITER`, `RICHTER`).
|
||||
* **OEPS-Daten (für Personen):**
|
||||
* `Satznummer`: 6-stellig, numerisch (Primärschlüssel OEPS).
|
||||
* `Lizenz`: Aktueller Lizenzcode (z.B. "R1").
|
||||
* `Startkarte`: Boolean/Status (Jahresgebühr bezahlt?).
|
||||
* `Verein_ID`: Verweis auf den Stammverein.
|
||||
* **Identifikatoren (Sonstige):**
|
||||
* `FEI-ID`
|
||||
* `Mitgliedsnummer_Zuchtverband`
|
||||
|
||||
---
|
||||
|
||||
### 6. Entität: `Pferd`
|
||||
|
||||
**Zweck:** Zentrale Verwaltung aller Pferde, egal ob im Sport oder in der Zucht.
|
||||
|
||||
**Beispiele:** Ein international erfolgreiches Sportpferd, eine Zuchtstute.
|
||||
|
||||
**Attribute:**
|
||||
* `Pferd-ID` (PK): Eindeutiger technischer Schlüssel (UUID).
|
||||
* `Name`: Offizieller Name des Pferdes.
|
||||
* `Abstammung_Vater_ID` (FK): Verweis auf ein anderes `Pferd` (Vater).
|
||||
* `Abstammung_Mutter_ID` (FK): Verweis auf ein anderes `Pferd` (Mutter).
|
||||
* `Besitzer_ID` (FK): Verweis auf den `Akteur`, dem das Pferd gehört.
|
||||
* **OEPS-Daten:**
|
||||
* `Satznummer`: 10-stellig, numerisch (Primärschlüssel OEPS).
|
||||
* `Kopfnummer`: 4-stellig, alphanumerisch (Permanente ID).
|
||||
* `Lebensnummer`: 9-stellig (Zuchtnummer).
|
||||
* **FEI-Daten:**
|
||||
* `FEI-ID`: Eindeutige FEI-Nummer.
|
||||
* `FEI-Pass`: Passnummer (kann abweichen).
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Lead Architect
|
||||
---
|
||||
# Entitäten des Kern-Modells
|
||||
|
||||
Dieses Verzeichnis enthält detaillierte Beschreibungen der zentralen fachlichen Entitäten des "Meldestelle"-Projekts.
|
||||
Jede Datei beschreibt eine Entität und ihre Attribute.
|
||||
|
||||
Diese Dokumente sind die "Wahrheit" für die Implementierung.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Prozess: ZNS-Import (Master Data Sync)
|
||||
|
||||
**Status:** Draft / Konzept
|
||||
**Datum:** 17.03.2026
|
||||
|
||||
## 1. Ausgangslage & Herausforderungen
|
||||
|
||||
Das OEPS stellt die Stammdaten als ZIP-Datei (`zns.zip`) bereit, die in Form von textbasierten ASCII-Dateien (Codepage
|
||||
850) vorliegen. Die Struktur ist starr, nicht relational und erfahrungsgemäß oft fehlerbehaftet oder unsauber
|
||||
formatiert (Legacy-Spezifikation V2.4).
|
||||
|
||||
Zusätzlich ändern sich Lizenzstände, Sperrlisten oder Registrierungen laufend. Weiters ist die Meldestelle oft
|
||||
gezwungen, vor Ort manuelle Korrekturen vorzunehmen oder Daten aus anderen Quellen (z.B. Zuchtverbände wie AWÖ) zu
|
||||
integrieren.
|
||||
|
||||
## 2. Architektonische Entscheidung: Event Sourcing & CQRS
|
||||
|
||||
Um den Anforderungen (vollständige Historie, turnierspezifischer Datenstand, fehlertoleranter Import, **manuelle
|
||||
Overrides**) gerecht zu werden, wird der ZNS-Import nach Prinzipien von **Event Sourcing** und **CQRS (Command Query
|
||||
Responsibility Segregation)** konzipiert.
|
||||
|
||||
* Wir überschreiben keine Daten einfach (`UPDATE`), sondern hängen Änderungen als Ereignisse (`EVENTS`) an.
|
||||
* Dies ermöglicht es uns, den Stand einer Person oder eines Pferdes für die Ewigkeit exakt zu rekonstruieren, selbst
|
||||
wenn sich die Stammdaten ändern.
|
||||
|
||||
### 2.1 Der Import-Ablauf (Die "Command" Seite)
|
||||
|
||||
1. **Ingestion:** Der User (Meldestelle) lädt die `zns.zip` hoch oder triggert einen Import aus einer anderen Quelle (
|
||||
Zuchtverband).
|
||||
2. **Parsing & Cleansing:** Ein dedizierter Importer-Service entpackt die ZIP, liest die Dateien zeilenweise (Codepage
|
||||
850!) und konvertiert die starren ASCII-Strings in nutzbare DTOs (Data Transfer Objects). Hier greifen erste
|
||||
Reinigungs-Routinen.
|
||||
3. **Event Generation:** Der Service vergleicht die geparsten Daten mit dem aktuellen Stand (der "Read Model"
|
||||
Datenbank).
|
||||
* Findet er einen neuen Akteur (Satznummer bisher unbekannt), erzeugt er ein `ActorCreatedEvent`.
|
||||
* Findet er Änderungen (z.B. Lizenz wurde von R1 auf R2 erhöht, oder Sperre wurde gesetzt), erzeugt er ein
|
||||
`ActorUpdatedEvent` (bzw. spezifischer `LicenseUpgradedEvent`, `ActorLockedEvent`).
|
||||
4. **Manuelle Korrekturen (Overrides):** Wenn die Meldestelle vor Ort Daten korrigiert (weil die OEPS-Daten falsch
|
||||
waren), erzeugt das System ein spezielles Event, z.B. `ManualActorCorrectionEvent`. Dieses Event hat eine **höhere
|
||||
Priorität** als zukünftige `ActorUpdatedEvents` aus dem ZNS-Import, solange der OEPS die Daten in seinem System nicht
|
||||
korrigiert hat (Lösung z.B. über einen "Ignorier-Zeitstempel" oder Prioritäts-Flags in der Projektion).
|
||||
5. **Event Log:** Diese Events werden in einem zentralen Event Log (dem "Event Store") persistiert. Dies ist die
|
||||
absolute Single Source of Truth.
|
||||
|
||||
### 2.2 Die Datenbereitstellung (Die "Query" Seite)
|
||||
|
||||
1. **Projection (Projektion):** Kleine "Listener" hören auf das Event Log und bauen daraus die relationale
|
||||
Lesedatenbank (SQLite / PostgreSQL) auf. Hierbei wird die Logik angewandt, dass manuelle Korrekturen der Meldestelle
|
||||
Vorrang vor veralteten Verbandsdaten haben.
|
||||
2. **Turnier-Snapshot:** Wenn ein Turnier konfiguriert wird oder am Vortag aktualisiert wird, zieht sich das System
|
||||
einen "Snapshot" (Schnappschuss) der aktuellen Stammdaten und verknüpft diese mit der Turnier-ID.
|
||||
3. **Zuchtverbands-Daten (Fremdformate):** Die Architektur erlaubt es uns leicht, neue Parser (z.B. für AWÖ-Daten) zu
|
||||
schreiben. Diese lesen die fremden Formate ein und generieren die gleichen Standard-Events (`HorseCreatedEvent`, aber
|
||||
evtl. mit Lebensnummer statt OEPS-Satznummer), die dann problemlos in die bestehende Projektion einfließen.
|
||||
|
||||
## 3. Datenhaltung (Konzeptuelles Modell)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant ImporterService as ZNS / AWÖ Importer
|
||||
participant EventStore as Event Log (Append Only)
|
||||
participant Projections as DB (Read Models)
|
||||
User ->> ImporterService: Upload zns.zip (Freitag vor Turnier)
|
||||
ImporterService ->> ImporterService: Parse & Clean
|
||||
|
||||
rect rgb(200, 220, 240)
|
||||
Note right of ImporterService: Generierung von Import-Events
|
||||
ImporterService ->> EventStore: Append: ActorUpdatedEvent(Satznummer: 123456, License: R2)
|
||||
end
|
||||
|
||||
User ->> EventStore: Manuelle Korrektur am Turniertag
|
||||
rect rgb(240, 200, 200)
|
||||
Note right of User: Generierung von Override-Events
|
||||
User ->> EventStore: Append: ManualActorCorrectionEvent(Satznummer: 123456, Name: "Neuer Name")
|
||||
end
|
||||
|
||||
EventStore -->> Projections: Update Relational DB (z.B. aktueller Stand)
|
||||
Note over Projections: Turniersystem liest nur <br/>aus Read Models. Manuelle Korrekturen<br/>gewinnen gegen Import-Daten.
|
||||
```
|
||||
|
||||
## 4. Vorteile dieser Architektur
|
||||
|
||||
* **Audit-Sicherheit:** Wir wissen exakt, *wann* sich *was* geändert hat. Nichts geht verloren.
|
||||
* **Archivierung:** Ein Turnier-Archiv muss nicht mehr mühsam als riesiger PDF/Daten-Dump gesichert werden. Wir können
|
||||
das Turnier einfach anhand des Timestamps gegen den Event Store abfragen.
|
||||
* **Fehlertoleranz:** Wenn ein Parsing-Fehler auftritt oder der OEPS kaputte Daten liefert, machen wir einfach ein
|
||||
Rollback der fehlerhaften Events und projizieren die Datenbank neu. Wir zerschießen nicht die operativen Tabellen.
|
||||
* **Erweiterbarkeit (Vision):** Wenn später ein "Ergebnis-Analyse-Service" oder ein "Züchter-Portal" angebunden wird,
|
||||
können diese einfach die historischen Events abonnieren (Kafka/Message Queue).
|
||||
* **Integration von Drittsystemen (Zuchtverbände):** Fremddaten können durch eigene Parser in unsere Standard-Events
|
||||
übersetzt und nahtlos integriert werden.
|
||||
|
||||
## 5. Zu klärende Details für die Implementierung
|
||||
|
||||
* **Sync-Mechanismus:** Wie kommen die Events vom Master-Server auf den Offline-Laptop im Plumpsklo? (Vermutlich eine
|
||||
robuste Sync-Queue, Kafka könnte für den Offline-Einsatz zu schwergewichtig sein, Alternativen evaluieren).
|
||||
* **Event-Payload:** Definition der JSON-Struktur für die wichtigsten Events (`zns.actor.updated`,
|
||||
`zns.horse.registered`).
|
||||
* **Merge-Logik:** Wie lange bleibt ein `ManualActorCorrectionEvent` gültig, bevor ein zukünftiges Update vom ZNS diesen
|
||||
Wert wieder überschreiben darf?
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Lead Architect
|
||||
---
|
||||
# Das Kern-Modell (Core Model)
|
||||
|
||||
Dieses Verzeichnis ist die "Single Source of Truth" für das destillierte, fachliche Wissen des Projekts. Nur was hier beschrieben ist, gilt als vereinbarte Wahrheit für die Implementierung.
|
||||
|
||||
## Struktur
|
||||
|
||||
* `Entities/`: Beschreibt die zentralen fachlichen Entitäten des Systems (z.B. Event, Turnier, Akteur).
|
||||
* `Processes/`: Dokumentiert die wichtigsten fachlichen Prozesse und Abläufe (z.B. Nennungsprozess, Ergebniserfassung).
|
||||
* `Rules/`: Definiert explizite Geschäftsregeln und Validierungen.
|
||||
|
||||
## Workflow
|
||||
|
||||
Informationen in diesem Verzeichnis sind das Ergebnis der Analyse von externen Quellen (siehe `../02_Reference`) und Workshops (siehe `../03_Analysis`).
|
||||
Jede Änderung am Core Model sollte nachvollziehbar und idealerweise durch ein ADR gestützt sein.
|
||||
Reference in New Issue
Block a user