chore: entferne veraltete Architekturdokumente

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
2026-05-05 21:22:46 +02:00
parent 6f15ada447
commit 15222b5453
258 changed files with 3388 additions and 6533 deletions
@@ -0,0 +1,170 @@
---
type: Concept
status: DRAFT
owner: Lead Architect
last_update: 2026-04-03
---
# Konzept: Offline-First Synchronisation (Desktop ↔ Backend)
## Ziel und Rahmen
Dieses Dokument definiert das Synchronisations-Konzept zwischen der Compose Desktop App (Meldestelle-Zentrale) und dem
Backend in einem Offline-First Szenario. Es baut auf ADR-0021 (Tenant-Isolation) und ADR-0022 (LAN-Sync mit
Lamport-Uhren) auf und erweitert sie um die WAN/Backend-Synchronisation.
Nicht-Ziele: Cloud-Realtime für Endnutzer, kollaboratives Editieren außerhalb des Veranstaltungsbetriebs.
## Leitprinzipien
1. Offline-First: Die Desktop-App ist voll funktionsfähig ohne Netzwerk; Synchronisation erfolgt opportunistisch.
2. Event-isoliert: Pro Veranstaltung eigener Datenraum (gemäß ADR-0021). Keine Vermischung von Events.
3. Einheitliches Änderungsmodell: Wiederverwendung des `SyncEvent`-Logs (ADR-0022) für Desktop↔Backend.
4. Domänen-Mastership: Klare Schreibhoheiten reduzieren Konflikte, fachliche Regeln haben Vorrang vor rein technischen
Timestamps.
5. Deterministische Konfliktauflösung: Lamport-Uhren + Regel-Matrix; keine Abhängigkeit von Systemuhren.
## Topologie & Rollen
- Backend (Zentrale Plattform):
- Master für: Stammdaten (Reiter, Pferde, Vereine, Funktionäre), Identität/Rollen, Gebührenkataloge, globale
Referenzen.
- Aggregations-/Archiv-Quelle nach Veranstaltungsende (finale Ergebnisse, Abrechnungen).
- Desktop (Meldestelle-Zentrale):
- Master während der Veranstaltung für: Nennungen (operativ), Startreihenfolgen, Startlisten-Status,
Ergebnisse/Protokolle (falls Richter nicht direkt am Backend), Kassa-Operationen vor Ort.
- Hält lokales `SyncEvent`-Log + Snapshots (vgl. ADR-0022) und synchronisiert mit Backend, sobald Konnektivität
besteht.
Hinweis Mehrfach-Desktops: Genau eine „Zentrale“ pro Veranstaltung besitzt Schreibhoheit (Konfig-Flag
`isEventAuthority=true`). Weitere Geräte sind Replikate/Clients.
## Datenkategorien & Mastership
| Kategorie | Master | Desktop Rechte | Backend Rechte |
|---------------------------|-----------------|----------------------------------------------|----------------------------------|
| Stammdaten (Actor) | Backend | Lesen, lokal „provisional“ anlegen (Temp-ID) | Vollzugriff, ID-Zuteilung, Merge |
| Veranstaltungs-Stammdaten | Backend | Lesen | Vollzugriff |
| Nennungen operativ | Desktop | Vollzugriff | Lesen, Import nach Sync |
| Startreihenfolge/Status | Desktop | Vollzugriff | Lesen, Import nach Sync |
| Bewertungen/Ergebnisse | Desktop/Richter | Vollzugriff (Eventzeitraum) | Lesen, Publikation/Archiv |
| Kassa/Finanzen vor Ort | Desktop | Vollzugriff | Lesen, Abgleich Summen |
Konflikte über Kategoriegrenzen werden durch Mastership-Regeln verhindert; verbleibende Konflikte werden per
Regel-Matrix gelöst.
## Änderungsmodell (Wiederverwendung `SyncEvent`)
Struktur wie in ADR-0022 beschrieben:
```kotlin
data class SyncEvent(
val eventId: String,
val turnierId: String?,
val sequenceNumber: Long, // Lamport
val originNodeId: String, // Desktop-ID oder Backend-Node-ID
val aggregateType: String, // z. B. "Nennung", "Bewertung", "Start"
val aggregateId: String,
val eventType: String,
val payload: ByteArray,
val createdAt: Instant,
val checksum: String,
val schemaVersion: Int,
)
```
- Erweiterung: `schemaVersion` ist Pflichtfeld für WAN-Sync (Schema-Evolution, Rolling Upgrades).
- Persistenz: `sync_events`, `sync_snapshots` lokal (SQLDelight) und im Backend (pro Tenant-Schema) gespiegelt.
## Lamport-Uhren & Vector-Clock (Optional)
- Primär: Lamport-Uhren wie ADR-0022. Gleichstand → lexikografisch größere `originNodeId` gewinnt (Determinismus).
- Optional für feingranulare Erkennung: Per-Aggregat Vector-Clock (`Map<nodeId, lamport>`) zur Diagnose;
Entscheidungsgrundlage bleibt Lamport + Fachregeln.
## Sync-Protokoll Desktop ↔ Backend (HTTPS)
Transport: HTTPS (HTTP/2), JSON oder Protobuf, idempotente Endpunkte. Auth: mTLS zwischen Desktop und Backend ODER
OAuth2 Client Credentials + Signatur der Batch-Payload.
Empfohlene Endpunkte (pro `eventId`):
```
POST /api/sync/{eventId}/hello → { nodeId, lastKnownSeq } → { backendNodeId, currentSeq, minSupportedSchema }
POST /api/sync/{eventId}/pull → { sinceSeq, limit } → [ SyncEvent... ], { nextSeq }
POST /api/sync/{eventId}/push → [ SyncEvent... ] → { ackedMaxSeq, rejected:[ids...] }
POST /api/sync/{eventId}/snapshot/request → { scope } → { snapshotBlob, snapshotSeq }
POST /api/sync/{eventId}/diagnostics → { stats } → { advice }
```
Batching: bis 512 Events oder 1 MiB pro Batch. Serverseitiges Paging über `sinceSeq`/`nextSeq`.
Idempotenz: Jeder `SyncEvent` wird durch `(eventId, originNodeId, sequenceNumber, checksum)` dedupliziert.
## Konfliktauflösung
1) Strukturkonflikte (gleiches Aggregat, konkurrierende Events):
- Wenn eine Seite nicht Master ist → Event wird angenommen, aber als `PENDING_REVIEW` markiert; fachliche Entscheidung
erforderlich (Backend-UI oder Desktop-Review-Queue).
- Beide Master (Sonderfälle, z. B. Ergebnisse während parallelem Backend-Fix):
- Lamport höher gewinnt.
- Gleichstand → `originNodeId`-Tiebreaker.
- Zusätzlich fachliche Heuristik optional: „Korrektur-Events“ (z. B. `ErgebnisKorrigiert`) schlagen normale
`ErgebnisErfasst` bei gleichem Lamport.
2) Identitätskonflikte (provisionale Stammdaten):
- Desktop darf temporäre Einträge (Temp-ID `tmp-...`) erzeugen.
- Beim Push führt Backend `Upsert+Merge` aus, weist finale IDs zu und liefert `IdMapping { tmpId -> finalId }` zurück.
- Desktop ersetzt Referenzen transaktional und emittiert ein lokales `IdRemapped`-Event (kein Re-Upload nötig, außer für
Diagnose).
3) Reihenfolge-/Kausalitätskonflikte:
- Bei fehlenden Vorgänger-Events antwortet Backend mit `rejected: [id]` und `requiredSinceSeq`. Desktop zieht Delta (
`pull`) und wiederholt den `push`.
## Snapshots & Recovery
- Snapshot-Intervall: standardmäßig 100 Events pro `(aggregateType, scope)` (wie ADR-0022), für WAN-Sync zusätzlich
Full-State-Snapshot pro Veranstaltung vor Event-Abschluss.
- Recovery: Desktop kann mit leerem Log starten → `snapshot/request` → Full-State + `snapshotSeq` → weitere Deltas über
`pull`.
- USB-Fallback (Notbetrieb): Export/Import von `sync_events` und `sync_snapshots` als verschlüsselte Archive (`.msync`).
Offene Spezifikation; separater PoC.
## Sicherheit
- Mandantentrennung: Jeder Request trägt `X-Event-Id` (ADR-0021). Backend validiert gegen `control.tenants`.
- Transport: `https` + mTLS (bevorzugt) oder `https` + OAuth2 Client Credentials. Payload-Signatur (HMAC-SHA256)
empfohlen.
- Integrität: `checksum` pro Event wird serverseitig geprüft; Mismatch → Reject.
- Rechte: Backend erzwingt Mastership-Regeln serverseitig; Verstöße → `PENDING_REVIEW` + Audit-Log.
## Fehlerfälle & Resilienz
- Netzwerkfehler: Exponentielles Backoff (bis 5 min), Offline-Queue unbegrenzt (bounded by disk quota), Telemetrie im
UI.
- Schema-Divergenz: `minSupportedSchema` aus `hello`; Desktop migriert vor weiterem Sync oder schaltet in Read-Only.
- Duplikate: Idempotenz-Keys verhindern Doppelverarbeitung. ACK enthält höchste verarbeitete `sequenceNumber`.
## Observability
- Metriken: `sync_push_events_total`, `sync_pull_events_total`, `sync_rejected_total`, `sync_latency_ms` (p50/p95),
`offline_duration_s`.
- Logs: pro Event `tenant`, `originNodeId`, `seq`, `aggType`, `eventType`, `result`.
- UI: Status-Anzeige (Verbunden, Getrennt, Ausstehend X), Konflikt-Review-Queue.
## Einführungsplan (Auszug)
1. Core: `SyncEvent` in Shared-KMP-Modul härten (`schemaVersion`), Persistenzschicht Desktop/Backend angleichen.
2. Backend-API: `hello/pull/push/snapshot` Endpunkte implementieren (Spring Boot/Ktor), Mandantentrennung.
3. Desktop-Client: Batch-Sync, Retry, Id-Mapping-Mechanismus.
4. Review-UI: `PENDING_REVIEW`-Queue im Backend (Admin) und Anzeige im Desktop.
5. E2E-Tests: Offline-Phase, Reconnect, Konflikt, Provisionals-Merge, Schema-Rolling-Upgrade.
## Referenzen
- [ADR-0021: Tenant-Resolution-Strategie](adr/0021-tenant-resolution-strategy-de.md)
- [ADR-0022: LAN-Sync-Protokoll (Meldestelle ↔ Richter-Turm)](adr/0022-lan-sync-protocol-de.md)
@@ -0,0 +1,100 @@
# Konzept: Zeitplan-Optimierung (Drag & Drop Logik)
> **Status:** ENTWURF | **Datum:** 11. April 2026
> **Autor:** 🏗️ Lead Architect
> **Kontext:** Phase 9 — Zeitplan & Protokollierung
## 1. Vision & Zielsetzung
Die Zeitplan-Optimierung ist das zentrale Werkzeug für die Meldestelle, um den Turnierablauf dynamisch an die
Gegebenheiten vor Ort anzupassen. Ziel ist eine intuitive, visuelle Oberfläche (Kalender-Ansicht), in der Bewerbe und
Abteilungen per Drag & Drop verschoben werden können, wobei das System automatisch Auswirkungen auf Folge-Bewerbe
berechnet und vor Konflikten warnt.
## 2. Fachliche Anforderungen (Use Cases)
### UC-1: Verschieben eines Bewerbs/einer Abteilung
- Ein Benutzer zieht einen Bewerb oder eine Abteilung auf eine neue Startzeit oder einen anderen Austragungsplatz.
- **System-Reaktion:**
- Neuberechnung der Endzeit basierend auf `reitdauerMinuten * starterAnzahl + besichtigungMinuten + umbauMinuten`.
- Prüfung auf Überschneidungen am selben Austragungsplatz.
- Warnung bei Konflikten (z.B. Richter-Doppelbelegung).
### UC-2: Dynamische Zeitplan-Anpassung („Anschließend“)
- Bewerbe können als `ANSCHLIESSEND` markiert werden (`beginnZeitTyp`).
- **System-Reaktion:** Wenn der vorangehende Bewerb verschoben wird oder länger dauert, verschiebt sich der
anschließende Bewerb automatisch mit.
### UC-3: Drag & Drop in der Startliste
- Innerhalb einer Startliste können Teilnehmer per Drag & Drop umsortiert werden.
- **System-Reaktion:** Automatische Aktualisierung der Kopfnummern (Startnummern) und Neuberechnung der individuellen
Startzeiten.
### UC-4: Protokollierung (Audit Log)
- Jede manuelle Änderung am Zeitplan oder der Startlisten-Reihenfolge muss protokolliert werden.
- **Grund:** Nachvollziehbarkeit bei Einsprüchen und Synchronisation zwischen verschiedenen Arbeitsplätzen (
Meldestelle ↔ Richterturm).
## 3. Datenmodell-Erweiterungen & Logik
### 3.1 Erweiterung `Bewerb` & `Abteilung`
Das bestehende Modell in `entries-domain` deckt bereits viele Felder ab. Für die Optimierung präzisieren wir:
- **`Umbauzeit`:** Zeit zwischen zwei Abteilungen oder Bewerben.
- **`Pausen`:** Geplante Unterbrechungen (z.B. Mittagspause, Platzpflege) werden als spezielle „Blocker-Events“ im
Zeitplan geführt.
- **`BesichtigungsBlock` (NEU):** Eigenständiges Objekt für Parcoursbesichtigungen.
- Kann mit mehreren Bewerben/Abteilungen verknüpft werden (Cross-Competition Inspection).
- Unterstützt Typen: `ZU_FUSS` (Standard) und `ZU_PFERD` (Spezialfall für Springpferde bis 110cm gemäß ÖTO).
- Validierung: Mindestens 5 Min. Puffer vor dem ersten Start (ÖTO §43).
### 3.2 Zeitberechnungs-Algorithmus (Präzisierung)
Die Logik in `StartlistenService.berechneStartzeiten` wird erweitert:
1. **Basis:** `Startzeit` der Abteilung.
2. **Vorlauf:** `besichtigungMinuten`.
3. **Starter-Loop:**
- `individuelleStartzeit = aktuelleZeit`.
- `aktuelleZeit += bewerb.reitdauerMinuten`.
- *Neu:* Berücksichtigung von festen Pausen nach X Startern (z.B. 10 Min. Pause alle 20 Starter).
4. **Nachlauf:** `umbauMinuten` am Ende der Abteilung/des Bewerbs.
### 3.3 Drag & Drop Logik (Frontend & Backend)
- **Frontend (Compose Desktop):**
- Implementierung eines `DraggableBewerbItem`.
- Visuelle Darstellung von Konflikten (Rote Markierung bei Überlappung).
- **Backend (API):**
- Neuer Endpunkt `PATCH /bewerbe/{id}/zeitplan` für schnelle Updates.
- Validierung der neuen Zeiten gegen den `austragungsplatzId` und `richterEinsaetze`.
## 4. Konflikt-Management
Das System arbeitet nach dem Prinzip **„Warnen statt Blockieren“** (ADR-0016):
- **Harte Fehler:** Nur bei technischer Unmöglichkeit (z.B. Datum in der Vergangenheit bei Live-Betrieb).
- **Warnungen:**
- Überlappung auf dem Platz.
- Richter hat gleichzeitig Einsatz in anderem Bewerb.
- Zeitunterschreitung für Reiter (Reiter startet in zwei kurz aufeinanderfolgenden Bewerben).
## 5. Synchronisation (Offline-First)
Änderungen am Zeitplan erzeugen `SyncEvents` (gemäß ADR-0022).
- **Lamport-Uhren:** Stellen sicher, dass bei gleichzeitigen Änderungen an zwei Laptops die zeitlich spätere Änderung (
oder die mit höherer Priorität) gewinnt.
- **Echtzeit-Update:** Über WebSockets werden Zeitplan-Änderungen sofort an alle verbundenen Clients (z.B.
Anzeige-Monitor, Richter-Tablet) gepusht.
## 6. Nächste Schritte
1. **Backend:** ✅ Implementiert (BewerbService, API, Zeitberechnungs-Pausen).
2. **Frontend:** Prototyping der Kalender-Ansicht mit Compose Desktop.
3. **QA:** Test-Szenarien für komplexe Verschiebungen (Kettenreaktionen bei „Anschließend“).
@@ -0,0 +1,67 @@
# Regelwerks-Check: Parcoursbesichtigung & Bewerbszusammenlegung
> **Status:** FINAL | **Datum:** 11. April 2026
> **Autor:** 📜 ÖTO/FEI Rulebook Expert
> **Kontext:** Zeitplan-Optimierung & Praxis-Szenarien
## 1. Parcoursbesichtigung zu Pferd
Die Aussage des Users wurde geprüft und bestätigt.
### 1.1 Regelwerks-Referenz (ÖTO 2026)
Gemäß **ÖTO Teil B, § 43 (Abweichungen)** gilt:
- **Normalfall:** Parcoursbesichtigung erfolgt zu Fuß.
- **Springpferde- & Jungpferdeprüfungen (bis 110 cm):** Eine Besichtigung **zu Pferd im Schritt** kann von der
Richtergruppe erlaubt werden.
- **Zweck:** Junge, unerfahrene Pferde sollen stressfrei an optische Reize (Fangständer, Farben, Unterbauten)
herangeführt werden.
### 1.2 Organisatorische Implikationen
- **Dauer:** Die Besichtigung zu Pferd benötigt oft etwas mehr Zeit für die Koordination am Einlass (15-20 Min. sind
praxisnah).
- **Sicherheit:** Da Pferde im Parcours sind, während andere Reiter eventuell noch draußen warten, muss der Zeitplan
einen Puffer von mindestens **5 Minuten** zwischen Ende Besichtigung und erstem Start vorsehen (ÖTO Vorschrift).
## 2. Zusammengelegte Besichtigungen (Cross-Competition Inspection)
In der Praxis üblich, wenn der Parcours für aufeinanderfolgende Bewerbe identisch bleibt.
### 2.1 Szenario
- **Bewerb A:** Springpferdeprüfung 105cm (Besichtigung zu Pferd erlaubt).
- **Bewerb B:** Standardspringprüfung 105cm (Besichtigung zu Fuß).
- **Lösung:** Eine gemeinsame Besichtigungszeit vor Bewerb A.
### 2.2 Regelwerks-Konformität
- Es gibt kein Verbot für gemeinsame Besichtigungen, solange die Teilnehmer beider Bewerbe die Möglichkeit haben, den
Parcours regelkonform zu besichtigen.
- **Wichtig:** Wenn Bewerb B erst 2 Stunden später startet, muss für Teilnehmer von Bewerb B theoretisch eine zweite
Besichtigungsmöglichkeit (oder ein "Refresh") angeboten werden, falls der Parcours zwischenzeitlich verändert wurde.
Wenn er identisch bleibt, reicht die einmalige Bekanntgabe.
## 3. Anforderungen für die Software (Zeitplan-Modul)
### 3.1 Datenmodell
- `Abteilung` oder `Bewerb` benötigt ein Flag `besichtigungZuPferdErlaubt` (Boolean).
- `BesichtigungsBlock` als eigenständiges Entitätsobjekt im Zeitplan, das mit **mehreren** Bewerben/Abteilungen
verknüpft werden kann.
### 3.2 Validierung & Warnungen
- **Warnung:** Wenn ein Besichtigungsblock mit "zu Pferd" markiert ist, aber ein verbundener Bewerb (z.B. L-Springen)
dies laut ÖTO normalerweise nicht vorsieht (Diskretionsspielraum der Richter).
- **Puffer-Check:** Automatische Prüfung, ob zwischen `Ende Besichtigung` und `Start Erster Reiter` mindestens 5 Minuten
liegen.
## 4. Fazit für Architect & Entwickler
Die "Besichtigung zu Pferd" ist ein valider Spezialfall für Springpferdeprüfungen. Die Software muss erlauben,
Besichtigungszeiten flexibel vor einen oder mehrere Bewerbe zu lagern und den Typ (Fuß/Pferd) zu kennzeichnen.
---
*Geprüft durch den 📜 ÖTO/FEI Rulebook Expert am 11.04.2026*
@@ -0,0 +1,62 @@
# 🤖 Konzept: Status-Automat für Nennungen & Zeitplan-Synchronisation
Dieses Dokument spezifiziert die Logik des Status-Automaten für Nennungen (Sprint C-1) und dessen Auswirkungen auf den
dynamischen Zeitplan.
## 1. Status-Definitionen (NennStatusE)
Basierend auf `core-domain/Enums.kt`:
| Status | Bedeutung | Auswirkung auf Zeitplan |
|:-------------------|:--------------------------------------------------|:--------------------------------------------------------|
| `EINGEGANGEN` | Nennung wurde erstellt (Initialzustand) | Belegt Zeitslot (basierend auf `reitdauerMinuten`) |
| `BESTAETIGT` | Meldestelle hat Nennung geprüft | Belegt Zeitslot |
| `NACHNENNUNG` | Nennung nach Nennschluss | Belegt Zeitslot (ggf. am Ende der Liste) |
| `TRANSFERIERT` | Nennung wurde auf anderes Paar übertragen | **Inaktiv** (Original-Eintrag wird durch neuen ersetzt) |
| `ZURUECKGEZOGEN` | Reiter hat abgemeldet (vor Startlistenerstellung) | **Inaktiv** (Slot wird frei) |
| `GESTARTET` | Paar ist in die Prüfung eingeritten | Startzeitpunkt fixiert, Folgestarts ggf. anpassen |
| `NICHT_ANGETRETEN` | Paar ist zum Startzeitpunkt nicht erschienen | **Zeitslot verfällt** oder Folgestarts rücken nach |
## 2. Status-Übergänge & Validierung
### 2.1 Gültige Übergänge (Beispiele)
- `EINGEGANGEN` -> `BESTAETIGT` (Normalfall)
- `EINGEGANGEN` -> `ZURUECKGEZOGEN` (Abmeldung)
- `BESTAETIGT` -> `TRANSFERIERT` (Reitertausch)
- `BESTAETIGT` -> `GESTARTET` (Während des Turniers)
### 2.2 Side-Effects (Side-Effect-Engine)
Wenn sich der Status einer Nennung ändert, müssen folgende Systeme informiert werden:
1. **Billing-Service:** Bei `ZURUECKGEZOGEN` ggf. Stornogebühren prüfen. Bei `TRANSFERIERT` Guthaben übertragen.
2. **Startlisten-Service:** Bei `ZURUECKGEZOGEN` nach Veröffentlichung der Startliste -> Eintrag als
`istGestrichen = true` markieren.
3. **Zeitplan-Optimierung:** Bei `NICHT_ANGETRETEN` -> Alle folgenden Startzeiten rücken um `reitdauerMinuten` nach
vorne (sofern im Kalender-Modus "Dynamisch" aktiv ist).
## 3. Dynamische Zeitplan-Anpassung (C-1 Extension)
Der `StartlistenService` muss eine Methode `aktualisiereZeitplanNachStatusAenderung` erhalten:
- **Szenario A (Nicht angetreten):** Wenn Nennung X `NICHT_ANGETRETEN` wird, rücken alle folgenden Nennungen in der
Abteilung nach vorne.
- **Szenario B (Verspätung):** Wenn Nennung X `GESTARTET` wird, aber 2 Minuten später als geplant, verschieben sich alle
Folgetermine um +2 Minuten (Kettenreaktion).
### Puffer-Regel (ÖTO-Konformität)
- Eine dynamische Verschiebung nach *vorne* darf nie dazu führen, dass ein Reiter vor seiner ursprünglich kommunizierten
Startzeit (oder einem definierten Puffer-Zeitraum von z.B. 15 Minuten) starten muss, ohne dass dies explizit bestätigt
wurde.
## 4. Implementierungs-Leitfaden für Backend (C-1)
1. Erweiterung von `NennungUseCases.statusAendern` um Aufrufe der Side-Effect-Handler.
2. Implementierung des `NennungStatusListener` in `entries-service`.
3. Anbindung an den `StartlistenService` zur Zeitre-Kalkulation.
---
**Status:** Entwurf (Lead Architect)
**Datum:** 11. April 2026