diff --git a/docs/01_Architecture/MASTER_ROADMAP.md b/docs/01_Architecture/MASTER_ROADMAP.md index 06a73a68..885302d7 100644 --- a/docs/01_Architecture/MASTER_ROADMAP.md +++ b/docs/01_Architecture/MASTER_ROADMAP.md @@ -2,7 +2,7 @@ type: Roadmap status: ACTIVE owner: Lead Architect -last_update: 2026-03-30 +last_update: 2026-04-03 --- # MASTER ROADMAP: Meldestelle-Biest @@ -13,6 +13,10 @@ last_update: 2026-03-30 Entwicklung einer ÖTO-konformen, offline-fähigen Turnier-Meldestelle als Compose Desktop App (KMP). Vollständige Self-Hosted Infrastruktur (Gitea, Pangolin, Zora). Datensouveränität, Offline-First, saubere Wissensbasis. +**Produktfokus:** +- Desktop-App ist der primäre Client (Compose Desktop, KMP) — „Desktop-First“ gilt für UX und Architektur. +- Offline-First Betrieb mit lokaler Persistenz und opportunistischer Synchronisation. + **Aktueller technischer Stand (30.03.2026):** * **Infrastruktur:** ✅ "Zora" (MS-R1, ARM64) ist live. Gitea & Registry laufen. * **Networking:** ✅ Pangolin Tunnel ersetzt Cloudflare. @@ -204,10 +208,12 @@ und über definierte Schnittstellen kommunizieren. *Ziel: Fachliche Tiefe in den Turnieren (Import, Generierung, Zeitberechnung).* +* [x] **Konzept/ADR:** LAN‑Sync (ADR‑0022) und Offline‑First Desktop↔Backend Konzept definiert und verlinkt. * [ ] **Bewerbe-Import:** Implementierung der Merge-Logik (ZNS-XML -> BewerbUiModel). * [ ] **Startlisten-Automatisierung:** Generierung und Zeitberechnung (Pausen, Umbauzeiten). * [ ] **Discovery:** Implementierung des mDNS-Service für die Geräte-Suche (Phase 7 Übertrag). * [ ] **Transport:** Aufbau der WebSocket-Infrastruktur für P2P-Sync (Phase 7 Übertrag). +* [ ] **Offline-First Desktop↔Backend:** Umsetzung gemäß Konzept „Offline-First Synchronisation (Desktop ↔ Backend)“ → `docs/01_Architecture/konzept-offline-first-desktop-backend-de.md`. ### PHASE 9: Series-Context & Erweiterungen 🔵 PHASE 2+ @@ -238,6 +244,8 @@ und über definierte Schnittstellen kommunizieren. | 13 | Masterdata: API-Schichten (REST vs. Ingestion) | ✅ | ADR-0019 | | 14 | Lokale Netzwerk-Kommunikation und Daten-Isolierung | ✅ | ADR-0020 | | 15 | Masterdata: Observability & Operations | ✅ | masterdata-ops.md, CHANGELOG | +| 16 | Tenant-Resolution: Schema-per-Tenant | ✅ | ADR-0021 | +| 17 | LAN-Sync-Protokoll (Lamport-Uhren, Event-Sourcing Light) | ✅ | ADR-0022 | --- diff --git a/docs/01_Architecture/konzept-offline-first-desktop-backend-de.md b/docs/01_Architecture/konzept-offline-first-desktop-backend-de.md new file mode 100644 index 00000000..ec468e54 --- /dev/null +++ b/docs/01_Architecture/konzept-offline-first-desktop-backend-de.md @@ -0,0 +1,147 @@ +--- +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`) 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) diff --git a/docs/04_Agents/Roadmaps/Architect_Roadmap.md b/docs/04_Agents/Roadmaps/Architect_Roadmap.md index 49d0c7df..a79bdfb9 100644 --- a/docs/04_Agents/Roadmaps/Architect_Roadmap.md +++ b/docs/04_Agents/Roadmaps/Architect_Roadmap.md @@ -39,15 +39,17 @@ ## 🟠 Sprint C — Priorität 2 (nächste Woche) - [ ] **C-1** | Synchronisations-Protokoll-Konzeption - - [ ] Offline-First-Konzept für Desktop ↔ Backend ausarbeiten - - [ ] Conflict-Resolution-Strategie definieren (gleichzeitige Änderungen) - - [ ] Konzept-Dokument in `docs/01_Architecture/` ablegen + - [x] Offline-First-Konzept für Desktop ↔ Backend ausarbeiten + - [x] Conflict-Resolution-Strategie definieren (gleichzeitige Änderungen) + - [x] Konzept-Dokument in `docs/01_Architecture/` ablegen → `docs/01_Architecture/konzept-offline-first-desktop-backend-de.md` + - Verweis/Bezug: Baut auf ADR-0021 (Tenant) und ADR-0022 (LAN-Sync Lamport) auf; einheitliches `SyncEvent`-Modell Desktop↔Backend. - [ ] **C-2** | MASTER_ROADMAP aktualisieren - - [ ] Desktop-App-Fokus eintragen - - [ ] Tenant-Isolation-Meilensteine (Sprint A Ergebnisse) als erledigt markieren - - [ ] Offline-Sync-Meilensteine eintragen - - [ ] Phase 8 Fortschritt reflektieren + - [x] Desktop-App-Fokus eintragen + - [x] Tenant-Isolation-Meilensteine (Sprint A Ergebnisse) als erledigt markieren + - [x] Offline-Sync-Meilensteine eintragen + - [x] Phase 8 Fortschritt reflektieren + - Update: Siehe `docs/01_Architecture/MASTER_ROADMAP.md` (Stand 2026-04-03) — Produktfokus ergänzt, ADR‑0021/0022 in ADR‑Tabelle eingetragen, Phase‑8‑Status („Konzept/ADR erledigt“) markiert, To‑do „Offline‑First Desktop↔Backend“ verlinkt. ---