Compare commits
5 Commits
6c64444a98
...
ef234747bc
| Author | SHA1 | Date | |
|---|---|---|---|
| ef234747bc | |||
| 7150622e1d | |||
| c6f28462eb | |||
| dbe7c74a9c | |||
| 6e484ee9a1 |
62
README.md
62
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# Meldestelle
|
||||
|
||||
> Modulares System für Pferdesportveranstaltungen — gebaut mit Domain-Driven Design, Kotlin Multiplatform und Microservices.
|
||||
> Desktop‑First Meldestelle: Offline‑fähige Compose‑Desktop‑App mit optionalem Backend‑Stack. Domänengetrieben (DDD), Kotlin Multiplatform.
|
||||
|
||||
[](https://git.mo-code.at/mocode-software/meldestelle/actions)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
|
@ -15,20 +15,28 @@ Die gesamte Projektdokumentation (Architektur, Fachdomäne, Entwickler-Anleitung
|
|||
|
||||
| Bereich | Inhalt |
|
||||
|-----------------------------------------------|---------------------------------------------|
|
||||
| [01_Architecture](./docs/01_Architecture) | Master Roadmap, ADRs, C4-Modelle |
|
||||
| [01_Architecture](./docs/01_Architecture) | Master Roadmap, ADRs, C4‑Modelle, Desktop‑Konzept |
|
||||
| [02_Guides](./docs/02_Guides) | Setup-Anleitungen, Entwickler-Guidelines |
|
||||
| [03_Domain](./docs/03_Domain) | Fachlichkeit, Turnierregeln, Entities |
|
||||
| [07_Infrastructure](./docs/07_Infrastructure) | Docker, Keycloak, CI/CD, Zora-Infrastruktur |
|
||||
|
||||
Wesentliche Architektur-Referenz: [Offline‑First Desktop & Backend (Kurzkonzept)](./docs/01_Architecture/konzept-offline-first-desktop-backend-de.md)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Tech Stack
|
||||
## 🖥️ Primärer Fokus: Desktop‑App
|
||||
|
||||
- Compose Multiplatform (JVM Desktop) als primäre Zielplattform
|
||||
- Offline‑First: Lokale SQLDelight‑DB, Synchronisation optional
|
||||
- Multi‑Tenant‑Backend optional für Sync/Verwaltung (Schema‑per‑Tenant, vgl. ADR‑0021)
|
||||
|
||||
## 🏗️ Tech Stack (aktueller Stand)
|
||||
|
||||
| Schicht | Technologie |
|
||||
|-----------------------|---------------------------------------------------|
|
||||
| **Backend** | Kotlin, Spring Boot 3.x, Spring Cloud Gateway |
|
||||
| **Frontend** | Kotlin Multiplatform (KMP), Compose Multiplatform |
|
||||
| **Datenbank** | PostgreSQL + Exposed / JPA |
|
||||
| **Frontend (primär)** | Kotlin Multiplatform (KMP), Compose Multiplatform |
|
||||
| **Backend** | Kotlin (Ktor/Spring Boot), Spring Cloud Gateway |
|
||||
| **Datenbank** | SQLDelight (lokal), PostgreSQL (Backend) |
|
||||
| **Auth** | Keycloak (OAuth2 / OIDC) |
|
||||
| **Cache** | Valkey |
|
||||
| **Service Discovery** | Consul |
|
||||
|
|
@ -38,27 +46,51 @@ Die gesamte Projektdokumentation (Architektur, Fachdomäne, Entwickler-Anleitung
|
|||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start (Lokal)
|
||||
## 🚀 Quick Start (lokale Entwicklung)
|
||||
|
||||
Empfohlen: Starte die Desktop‑App direkt aus dem Repo. Der Backend‑Stack ist optional und wird nur für Sync/Integration benötigt.
|
||||
|
||||
### A) Desktop‑App starten (ohne Backend)
|
||||
|
||||
Voraussetzungen: JDK 21+, aktuelle Gradle Wrapper verwendet.
|
||||
|
||||
```bash
|
||||
# 1. Umgebungsvariablen vorbereiten (nur beim ersten Mal)
|
||||
cp .env.example .env
|
||||
# Desktop‑Shell ausführen (Compose Desktop)
|
||||
./gradlew :frontend:shells:meldestelle-desktop:run
|
||||
```
|
||||
|
||||
# 2. Infrastruktur starten (Postgres, Keycloak, Valkey, Consul, Zipkin)
|
||||
Hinweise:
|
||||
- Beim ersten Start wird die lokale Datenbank initialisiert (Offline‑First).
|
||||
- Architektur-Referenz: `docs/06_Frontend/MVVM_UDF_Pattern.md` (UDF/MVVM für ViewModels)
|
||||
|
||||
### B) Optional: Backend‑Stack per Docker starten
|
||||
|
||||
```bash
|
||||
# 1. Umgebungsvariablen (nur beim ersten Mal)
|
||||
cp .env.example .env || true
|
||||
|
||||
# 2. Infrastruktur (Postgres, Keycloak, Valkey, Consul, Zipkin)
|
||||
docker compose -f docker-compose.yaml -f dc-infra.yaml up -d
|
||||
|
||||
# 3. Backend-Services starten (Gateway, Ping-Service)
|
||||
# 3. Backend‑Services (Gateway, Masterdata, Ping, …)
|
||||
docker compose -f docker-compose.yaml -f dc-backend.yaml up -d
|
||||
|
||||
# 4. Ops-Stack starten (Prometheus, Grafana)
|
||||
# 4. Ops‑Stack (Prometheus, Grafana)
|
||||
docker compose -f docker-compose.yaml -f dc-ops.yaml up -d
|
||||
|
||||
# 5. Optional: Web-App starten
|
||||
# 5. Optional: Web‑Shell
|
||||
docker compose -f docker-compose.yaml -f dc-gui.yaml up -d
|
||||
```
|
||||
|
||||
> ⚠️ **Reihenfolge beachten:** Infra muss `healthy` sein, bevor Backend gestartet wird.
|
||||
> Keycloak benötigt ~60–90 Sekunden zum Hochfahren.
|
||||
> ⚠️ Reihenfolge beachten: Infra muss `healthy` sein, bevor Backend gestartet wird. Keycloak benötigt ~60–90 Sekunden.
|
||||
|
||||
---
|
||||
|
||||
## 🧭 Legacy (V1) Hinweise
|
||||
|
||||
- Ältere V1‑Abschnitte/Anleitungen (Web‑First/Microservices‑Only) gelten als DEPRECATED.
|
||||
- Verwende für lokale Entwicklung primär die Desktop‑App (siehe Quick Start). Docker‑Stacks sind optional für Integration/Sync.
|
||||
- In der Doku sind V1‑Seiten entsprechend markiert oder werden sukzessive bereinigt (siehe Roadmaps Sprint C‑4).
|
||||
|
||||
### Wichtige lokale Ports
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
78
docs/01_Architecture/Proposals/C-3_Docs-Strukturvorschlag.md
Normal file
78
docs/01_Architecture/Proposals/C-3_Docs-Strukturvorschlag.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
type: [Proposal]
|
||||
status: [DRAFT]
|
||||
owner: [🏗️ Lead Architect, 🧹 Curator]
|
||||
created: 2026-04-03
|
||||
related:
|
||||
- docs/01_Architecture/MASTER_ROADMAP.md
|
||||
- docs/04_Agents/Roadmaps/Curator_Roadmap.md
|
||||
---
|
||||
|
||||
# C-3: Vorschlag zur Unterordner-Struktur in `docs/`
|
||||
|
||||
## Ziel
|
||||
Überladene/uneinheitliche Verzeichnisse identifizieren und eine klare, skalierbare Struktur vorschlagen, die zu unseren Prinzipien „Docs‑as‑Code“ und „Offline‑First“ passt. Keine Moves im Rahmen dieses PRs – nur Abstimmungsvorlage.
|
||||
|
||||
## Leitprinzipien
|
||||
- One purpose per folder: thematische Trennung (Architektur, Domain, Backend, Frontend, Infrastruktur, Prozesse).
|
||||
- Assets trennen: Binärdateien/Medien unter `assets/` konsolidieren, fachliche Inhalte bleiben textuell in Bereichsordnern.
|
||||
- Zeitliche Inhalte standardisieren: Tägliche Notizen → `99_Journal/`, formelle Auswertungen/Status → `90_Reports/`.
|
||||
- Reversibel: Bestehende Links möglichst stabil halten; bei Moves Redirect-Hinweise (Link-Stub) lassen.
|
||||
|
||||
## Hotspots (Ist-Zustand)
|
||||
- `06_Frontend` (20+ Dateien, 10+ Unterordner): Fach-Docs, Logs, Screenshots und ein ZIP (FIGMA) gemischt.
|
||||
- `99_Journal`: viele Session-Logs – erwartbar groß, aber Index fehlt.
|
||||
- `90_Reports`: Berichte gemischt ohne Subkategorien.
|
||||
- Lose Ordner im Doc-Root ohne klare Zuordnung: `BilderSuDo/`, `ScreenShots/`, `temp/`, `OePS/`, `Neumarkt2026/`, `Bin/`.
|
||||
|
||||
## Zielstruktur (Top-Level, unverändert wo sinnvoll)
|
||||
- `01_Architecture/` – ADRs, Referenzen, Roadmaps, Proposals (neu: dieser Ordner)
|
||||
- `02_Guides/` – Setup- & How‑To‑Guides
|
||||
- `03_Domain/` – Fachliche Referenzen, Glossar, Regelwerke
|
||||
- `04_Agents/` – Playbooks, Roadmaps, Sitzungsdokus der Agenten
|
||||
- `05_Backend/` – API, DB‑Schema, Services‑Doku
|
||||
- `06_Frontend/` – Patterns, Navigation, Guidelines (+ interner `README`/Index)
|
||||
- `07_Infrastructure/` – Deployment, Ops, Security
|
||||
- `80_Assets/` – NEU: Zentrale Medien & Binärartefakte
|
||||
- `80_Assets/screenshots/`
|
||||
- `80_Assets/figma/`
|
||||
- `80_Assets/exports/` (z. B. HTML‑Templates, generierte Artefakte)
|
||||
- `80_Assets/legacy/` (Altbestände wie `BilderSuDo`)
|
||||
- `90_Reports/` – Berichte, gegliedert nach Bereich/Projekt
|
||||
- `90_Reports/frontend/`
|
||||
- `90_Reports/backend/`
|
||||
- `90_Reports/events/` (z. B. `Neumarkt2026/`)
|
||||
- `99_Journal/` – Chronologisches Journal (täglich), mit monatlichen Indexdateien
|
||||
- `_archive/` – Altes/abgelegtes Material (mit READMEs, warum archiviert)
|
||||
|
||||
## Migrationsmatrix (Quelle → Ziel)
|
||||
- `docs/06_Frontend/FIGMA/**/*.zip` → `docs/80_Assets/figma/`
|
||||
- `docs/06_Frontend/Screenshots/**/*` → `docs/80_Assets/screenshots/frontend/`
|
||||
- `docs/06_Frontend/Logs/**/*` → `docs/90_Reports/frontend/logs/`
|
||||
- `docs/06_Frontend/StartErgListen/*.html|*.png` → `docs/80_Assets/exports/erg-listen/`
|
||||
- `docs/ScreenShots/**/*` → `docs/80_Assets/screenshots/`
|
||||
- `docs/BilderSuDo/**/*` → `docs/80_Assets/legacy/BilderSuDo/`
|
||||
- `docs/Neumarkt2026/**/*` → `docs/90_Reports/events/Neumarkt2026/`
|
||||
- `docs/OePS/**/*` → `docs/03_Domain/02_Reference/OEPS/`
|
||||
- `docs/Bin/**/*` → `docs/_archive/bin/` (oder `platform/tools/` sofern build-relevant; Klärung nötig)
|
||||
- `docs/temp/**/*` → `docs/_archive/_temp/` (oder löschen, falls leer/obsolet)
|
||||
|
||||
## Phasenplan
|
||||
1) Phase 1 (dieses Ticket): Inventur, Vorschlag, Abstimmung. Keine Moves, nur Verlinkungen/Indexe ergänzen.
|
||||
2) Phase 2 (nach Freigabe): Gezielte Moves inkl. Redirect‑Stubs (kurze MD-Dateien mit Verweis), Link-Update in Kern-Docs.
|
||||
3) Phase 3 (Cleanup): Veraltete Duplikate entfernen oder als `ARCHIVED` markieren; monatliche Indizes für `99_Journal` einführen.
|
||||
|
||||
## Offene Punkte für den Architect
|
||||
- `Bin/`: Enthält es build-relevante Tools? Falls ja, Verlagerung in `platform/` statt `_archive`.
|
||||
- `OePS/`: Exakter Zielpfad unter `03_Domain/02_Reference/` – Naming „OEPS“ vs. „OePS“ vereinheitlichen.
|
||||
- HTML‑Templates (`StartErgListen`): Bleiben sie als Referenz im Frontend oder als Export/Asset?
|
||||
- Fallback für große Binärdateien (>50 MB): Soll ein externes Artefakt-Repo genutzt werden?
|
||||
|
||||
## Auswirkungen
|
||||
- Klare Trennung zwischen Text‑Doku und Medien → bessere Lesbarkeit, schnellere Diffs.
|
||||
- Bessere Skalierbarkeit der zeitlichen Inhalte (Reports/Journal) → einfache Auffindbarkeit.
|
||||
- Geringes Bruchrisiko dank Redirect‑Stubs in Phase 2.
|
||||
|
||||
## Nächste Schritte
|
||||
- Review durch 🏗️ Architect
|
||||
- Nach Freigabe: Migrations-Tickets je Teilbereich (Frontend, Reports, Assets)
|
||||
147
docs/01_Architecture/konzept-offline-first-desktop-backend-de.md
Normal file
147
docs/01_Architecture/konzept-offline-first-desktop-backend-de.md
Normal file
|
|
@ -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<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)
|
||||
|
|
@ -7,14 +7,19 @@ tags: [setup, local, docker, gradle]
|
|||
|
||||
# Start Local (Lokales Setup)
|
||||
|
||||
Kurzanleitung, um das Projekt lokal in wenigen Minuten zu starten.
|
||||
Kurzanleitung, um das Projekt lokal in wenigen Minuten zu starten – Desktop‑First mit optionalem Docker‑Backend.
|
||||
|
||||
## Voraussetzungen
|
||||
- Docker und Docker Compose (v2)
|
||||
- Java 25 (JDK)
|
||||
- Git
|
||||
## Voraussetzungen (exakte Versionen)
|
||||
- Git ≥ 2.40
|
||||
- JDK 25 (Temurin/Eclipse Adoptium empfohlen) – Projekttoolchain lädt bei Bedarf automatisch
|
||||
- Gradle Wrapper 9.4.0 (wird über `./gradlew` automatisch verwendet)
|
||||
- Docker Engine ≥ 24, Docker Compose v2 ≥ 2.24
|
||||
|
||||
## Schnellstart
|
||||
Hinweise:
|
||||
- Die Java‑Toolchain wird per Gradle automatisch heruntergeladen (`org.gradle.java.installations.auto-download=true`). Ein lokal installiertes JDK 25 wird dennoch empfohlen für IDE‑Runs.
|
||||
- Auf Apple‑Silicon (arm64) sind die Docker‑Images optimiert; Keycloak nutzt `start --optimized`.
|
||||
|
||||
## Schnellstart (nur Backend in Docker)
|
||||
|
||||
```bash
|
||||
# 1) Repository klonen
|
||||
|
|
@ -25,7 +30,7 @@ cd meldestelle
|
|||
# Kopiere die Vorlage.
|
||||
cp .env.example .env
|
||||
|
||||
# 3) Infrastruktur starten (Postgres, Redis, Keycloak, Monitoring, Gateway)
|
||||
# 3) Infrastruktur starten (Postgres, Valkey, Keycloak, Tracing, Service Discovery)
|
||||
docker compose --profile infra up -d
|
||||
|
||||
# 4) Backend starten (Gateway + Ping Service)
|
||||
|
|
@ -33,10 +38,25 @@ docker compose --profile backend up -d
|
|||
```
|
||||
|
||||
Sobald die Infrastruktur läuft, erreichst du unter anderem:
|
||||
- Gateway: http://localhost:8081
|
||||
- Keycloak: http://localhost:8180
|
||||
- Grafana: http://localhost:3000
|
||||
- Prometheus: http://localhost:9090
|
||||
- Gateway (API): http://localhost:8081
|
||||
- Keycloak (IAM): http://localhost:8180
|
||||
- Zipkin (Tracing): http://localhost:9411
|
||||
- Consul (Service Discovery): http://localhost:8500
|
||||
- Optional Web‑App (falls `--profile gui` gebaut/gestartet): http://localhost:4000
|
||||
|
||||
## Desktop‑App starten (Compose Desktop)
|
||||
|
||||
Die Desktop‑App ist der primäre Entwicklungs‑Entry‑Point.
|
||||
|
||||
```bash
|
||||
# 1) Abhängigkeiten bauen (optional; Gradle lädt automatisch beim ersten Run)
|
||||
./gradlew :frontend:shells:meldestelle-desktop:build
|
||||
|
||||
# 2) Desktop‑App starten
|
||||
./gradlew :frontend:shells:meldestelle-desktop:run
|
||||
```
|
||||
|
||||
Voraussetzung: Für Features, die Backend‑Konnektivität benötigen (z. B. Login, Stammdaten), muss das Docker‑Backend (infra + backend) laufen. Für rein lokale/offline Flows kann die App auch ohne Docker gestartet werden.
|
||||
|
||||
## Tests ausführen
|
||||
```bash
|
||||
|
|
@ -58,9 +78,12 @@ Sobald die Infrastruktur läuft, erreichst du unter anderem:
|
|||
docker compose down -v
|
||||
docker compose --profile infra up -d
|
||||
```
|
||||
- Environment-Variablen: werden aus der `.env`-Datei im Root-Verzeichnis geladen.
|
||||
- Environment‑Variablen: werden aus der `.env`‑Datei im Root‑Verzeichnis geladen.
|
||||
- Gradle/Java Probleme? Stelle sicher, dass JDK 25 aktiv ist bzw. lasse die Gradle‑Toolchain das passende JDK laden.
|
||||
- ARM64 (Apple Silicon): Falls Images nicht starten, lösche alte Images und starte neu: `docker compose down -v && docker system prune -af && docker compose --profile infra up -d`.
|
||||
|
||||
## Weiterführende Hinweise
|
||||
- Architektur: `docs/01_Architecture/MASTER_ROADMAP_2026_Q1.md`
|
||||
- README Quick‑Start (Desktop‑First): `README.md`
|
||||
- Architektur‑Kurzkonzept (Offline‑First Desktop & Backend): `docs/01_Architecture/konzept-offline-first-desktop-backend-de.md`
|
||||
- ADRs: `docs/01_Architecture/adr/`
|
||||
- Aktuelle Reports: `docs/90_Reports/`
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -42,33 +42,33 @@
|
|||
- [x] Alle Roadmaps: abgeschlossene Aufgaben korrekt als `[x]` markiert
|
||||
|
||||
- [ ] **B-2** | `docs/05_Backend/` aktualisieren
|
||||
- [ ] Datenbankschema dokumentieren: Tabellen `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`,
|
||||
`teilnehmer_konten`, `turnier_kassa` (Flyway V1–V009)
|
||||
- [ ] API-Endpunkte-Übersicht erstellen: Reiter, Pferde, Vereine, Funktionäre (Backend B-1 ✅ abgeschlossen)
|
||||
- [ ] Kassa-Endpunkte ergänzen sobald Backend B-2 abgeschlossen (`/kassa/saldo`, `/zahlvorgaenge`)
|
||||
- [ ] Tenant-Isolation (ADR-0021) und Multi-Tenant-Architektur kurz beschreiben
|
||||
- [x] Datenbankschema dokumentieren: Tabellen `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`,
|
||||
`teilnehmer_konten`, `turnier_kassa` (Flyway V1–V009) → `docs/05_Backend/Schema/Database_Schema_V1-V009.md` (03.04.2026)
|
||||
- [x] API-Endpunkte-Übersicht erstellen: Reiter, Pferde, Vereine, Funktionäre (Backend B-1 ✅ abgeschlossen) → `docs/05_Backend/API/API_Uebersicht_Stammdaten.md` (03.04.2026)
|
||||
- [ ] Kassa-Endpunkte ergänzen sobald Backend B-2 abgeschlossen (`/kassa/saldo`, `/zahlvorgaenge`) → Platzhalter: `docs/05_Backend/API/Kassa_API.md` (DRAFT)
|
||||
- [x] Tenant-Isolation (ADR-0021) und Multi-Tenant-Architektur kurz beschreiben → `docs/05_Backend/Multi_Tenant_Kurz.md` (03.04.2026)
|
||||
|
||||
- [ ] **B-3** | `docs/06_Frontend/` aktualisieren
|
||||
- [ ] ViewModel-Architektur-Muster (MVVM/UDF) verlinken
|
||||
- [ ] Verweis auf `VeranstalterViewModel` als Referenz-Implementierung eintragen
|
||||
- [x] ViewModel-Architektur-Muster (MVVM/UDF) verlinken → `docs/06_Frontend/MVVM_UDF_Pattern.md` (03.04.2026)
|
||||
- [x] Verweis auf `VeranstalterViewModel` als Referenz-Implementierung eintragen → Code: `frontend/features/veranstalter-feature/src/commonMain/kotlin/at/mocode/veranstalter/feature/presentation/VeranstalterViewModel.kt` (03.04.2026)
|
||||
|
||||
---
|
||||
|
||||
## 🟠 Sprint C — Priorität 2 (nächste Woche)
|
||||
|
||||
- [ ] **C-1** | `README.md` aktualisieren
|
||||
- [ ] Desktop-App als primären Fokus hervorheben
|
||||
- [ ] Schnellstart-Anleitung für lokale Entwicklungsumgebung prüfen
|
||||
- [ ] Veraltete V1-Abschnitte entfernen oder als deprecated markieren
|
||||
- [x] Desktop-App als primären Fokus hervorheben → `README.md` (03.04.2026)
|
||||
- [x] Schnellstart-Anleitung für lokale Entwicklungsumgebung prüfen → Desktop-Run (`:frontend:shells:meldestelle-desktop:run`) ergänzt (03.04.2026)
|
||||
- [x] Veraltete V1-Abschnitte entfernen oder als deprecated markieren → Abschnitt „Legacy (V1) Hinweise“ in `README.md` (03.04.2026)
|
||||
|
||||
- [ ] **C-2** | Setup-Guide aktualisieren
|
||||
- [ ] Schritt-für-Schritt: Projekt klonen → Docker starten → Desktop-App starten
|
||||
- [ ] Voraussetzungen (JDK, Gradle, Docker) mit exakten Versionen dokumentieren
|
||||
- [ ] Dokument in `docs/02_Guides/` ablegen
|
||||
- [x] **C-2** | Setup-Guide aktualisieren ✅ *3. April 2026*
|
||||
- [x] Schritt-für-Schritt: Projekt klonen → Docker starten → Desktop-App starten → `docs/02_Guides/start-local.md`
|
||||
- [x] Voraussetzungen (JDK, Gradle, Docker) mit exakten Versionen dokumentiert (JDK 25, Gradle 9.4.0, Compose v2)
|
||||
- [x] Dokument in `docs/02_Guides/` abgelegt/aktualisiert → `docs/02_Guides/start-local.md`
|
||||
|
||||
- [ ] **C-3** | Unterordner-Struktur in `docs/` prüfen
|
||||
- [ ] Überladene Verzeichnisse identifizieren
|
||||
- [ ] Strukturvorschlag mit Architect abstimmen
|
||||
- [x] Überladene Verzeichnisse identifizieren → Hotspots dokumentiert (06_Frontend, 99_Journal, 90_Reports, BilderSuDo, ScreenShots, temp, OePS, Neumarkt2026, Bin) (03.04.2026)
|
||||
- [ ] Strukturvorschlag mit Architect abstimmen → Proposal: `docs/01_Architecture/Proposals/C-3_Docs-Strukturvorschlag.md`
|
||||
|
||||
- [ ] **C-4** | V1-Code-Bereinigung koordinieren
|
||||
- [ ] V1-Dateien und -Module zusammen mit Frontend + Backend identifizieren
|
||||
|
|
|
|||
63
docs/05_Backend/API/API_Uebersicht_Stammdaten.md
Normal file
63
docs/05_Backend/API/API_Uebersicht_Stammdaten.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Backend Developer
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
# API-Übersicht Stammdaten
|
||||
|
||||
Abdeckung: Backend Sprint B‑1 (abgeschlossen). Konsolidierte Endpunkte für Reiter, Pferde, Vereine und Funktionäre. Implementiert mit Ktor; DTOs und Validierung in den jeweiligen Services.
|
||||
|
||||
Hinweis Tenant/Headers: Stammdaten sind global bzw. nicht tenant‑spezifisch. Für domain‑spezifische Vorgänge (Entries, Kassa) gilt ADR‑0021/`X-Event-Id`.
|
||||
|
||||
## Reiter (`/reiter`)
|
||||
|
||||
- GET `/reiter` — Liste (optional Filter: `lizenzKlasse`, `vereinId`; Pagination: `limit`, `offset`)
|
||||
- GET `/reiter/search?q=...` — Suche nach Name oder Satznummer
|
||||
- GET `/reiter/{id}` — Einzelabruf
|
||||
- GET `/reiter/satznummer/{nr}` — Lookup per Satznummer
|
||||
- POST `/reiter` — Anlegen
|
||||
- PUT `/reiter/{id}` — Aktualisieren (partiell)
|
||||
- DELETE `/reiter/{id}` — Löschen
|
||||
|
||||
Quelle: `backend/services/masterdata/masterdata-api/.../ReiterController.kt`
|
||||
|
||||
## Pferde (`/horse`)
|
||||
|
||||
- GET `/horse` — Liste (optional Filter: `jahrgang`, `besitzerId`; Pagination: `limit`, `offset`)
|
||||
- GET `/horse/search?q=...` — Suche nach Name/Nummern
|
||||
- GET `/horse/{id}` — Einzelabruf
|
||||
- POST `/horse` — Anlegen
|
||||
- PUT `/horse/{id}` — Aktualisieren (partiell)
|
||||
- DELETE `/horse/{id}` — Löschen
|
||||
|
||||
Quelle: `backend/services/masterdata/masterdata-api/.../HorseController.kt`
|
||||
|
||||
## Vereine (`/verein`)
|
||||
|
||||
- GET `/verein` — Liste (optional Filter: `verband`/Bundesland; Pagination: `limit`, `offset`)
|
||||
- GET `/verein/search?q=...` — Suche nach Name/Kurzname
|
||||
- GET `/verein/{id}` — Einzelabruf
|
||||
- POST `/verein` — Anlegen
|
||||
- PUT `/verein/{id}` — Aktualisieren (partiell)
|
||||
- DELETE `/verein/{id}` — Löschen
|
||||
|
||||
Quelle: `backend/services/masterdata/masterdata-api/.../VereinController.kt`
|
||||
|
||||
## Funktionäre (`/funktionaer`)
|
||||
|
||||
- GET `/funktionaer` — Liste (optional Filter: `rolle`; Pagination: `limit`, `offset`)
|
||||
- GET `/funktionaer/search?q=...` — Suche nach Name
|
||||
- GET `/funktionaer/{id}` — Einzelabruf
|
||||
- POST `/funktionaer` — Anlegen
|
||||
- PUT `/funktionaer/{id}` — Aktualisieren (partiell)
|
||||
- DELETE `/funktionaer/{id}` — Löschen
|
||||
|
||||
Quelle: `backend/services/masterdata/masterdata-api/.../FunktionaerController.kt`
|
||||
|
||||
## Standards
|
||||
|
||||
- Auth: Standard-Bearer optional je nach Deploymentprofil (siehe Service-Konfig)
|
||||
- Content-Type: `application/json; charset=utf-8`
|
||||
- Fehlerfälle: 400 (Validierung), 404 (nicht gefunden)
|
||||
37
docs/05_Backend/API/Kassa_API.md
Normal file
37
docs/05_Backend/API/Kassa_API.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
type: Reference
|
||||
status: DRAFT
|
||||
owner: Backend Developer
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
# Kassa-API (Entwurf)
|
||||
|
||||
Abhängigkeit: Backend Sprint B‑2 (Kassa-Service) — laut Curator‑Roadmap noch offen. Dieses Dokument reserviert die Endpunkte und beschreibt die Erwartungen. Die Implementierungsdetails werden ergänzt, sobald der Service fertiggestellt ist.
|
||||
|
||||
Mandantenkontext: Erfordert `X-Event-Id` (ADR‑0021). Alle Kassa‑Operationen sind tenant‑lokal.
|
||||
|
||||
## Endpunkte (geplant)
|
||||
|
||||
- GET `/kassa/saldo` — Liefert den aktuellen Saldo der Veranstaltung und optional pro Turnier.
|
||||
- Query: `turnierId` (optional, UUID)
|
||||
- Response: `{ saldoCents: long, currency: "EUR", scope: "veranstaltung"|"turnier", turnierId?: UUID }`
|
||||
|
||||
- GET `/zahlvorgaenge` — Listet Kassa‑Buchungen/Zahlvorgänge (paginiert, filterbar).
|
||||
- Query: `turnierId?`, `teilnehmerId?`, `typ?` (EINZAHLUNG|AUSZAHLUNG|KORREKTUR), `limit`, `offset`
|
||||
- Response: Liste von Transaktionen mit Betrag, Zeit, Verweis (Teilnehmer/Turnier), Notiz
|
||||
|
||||
- POST `/zahlvorgaenge` — Erstellt einen neuen Zahlungsvorgang (Ein-/Auszahlung/Korrektur).
|
||||
- Body: `{ typ, betragCents, currency, referenz: { teilnehmerId?|turnierId? }, notiz? }`
|
||||
- Wirkung: Aktualisiert `teilnehmer_konten` bzw. `turnier_kassa` atomar.
|
||||
|
||||
## Validierung & Regeln (voraussichtlich)
|
||||
|
||||
- Atomare Konsistenz: Transaktion + Saldo‑Update innerhalb einer DB‑Transaktion.
|
||||
- Negativsaldo optional durch Feature‑Flag blockierbar.
|
||||
- Audit‑Trail pro Vorgang (who/when), Idempotenz‑Key optional.
|
||||
|
||||
## Implementierungsstand
|
||||
|
||||
- Datenstrukturen `teilnehmer_konten` und `turnier_kassa` sind per Flyway angelegt (siehe [Datenbankschema](../Schema/Database_Schema_V1-V009.md)).
|
||||
- Endpunkte folgen nach Abschluss Backend B‑2. Dieses Dokument wird dann auf `status: ACTIVE` gesetzt.
|
||||
28
docs/05_Backend/Multi_Tenant_Kurz.md
Normal file
28
docs/05_Backend/Multi_Tenant_Kurz.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Lead Architect
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
# Tenant-Isolation & Multi‑Tenant (Kurzfassung)
|
||||
|
||||
Vollständige Entscheidung: [ADR‑0021: Tenant‑Resolution‑Strategie (Schema‑per‑Tenant)](../01_Architecture/adr/0021-tenant-resolution-strategy-de.md).
|
||||
|
||||
## Kernaussagen
|
||||
|
||||
- Eine Veranstaltung = ein Tenant = ein Datenbankschema (Schema‑per‑Tenant).
|
||||
- Requests tragen `X-Event-Id`; Backend validiert gegen `control.tenants` und schaltet das Schema je Request.
|
||||
- Flyway führt Migrationen je Tenant‑Schema aus (eigene `flyway_schema_history`).
|
||||
- Stammdaten (Reiter/Pferde/Vereine/Funktionäre) sind global und nicht tenant‑spezifisch; Entries/Kassa sind tenant‑lokal.
|
||||
|
||||
## Umsetzung (Kurz)
|
||||
|
||||
- Web‑Layer: `TenantWebFilter` (Spring) bzw. Plugin (Ktor) liest `X-Event-Id` und legt `TenantContext` ab.
|
||||
- Persistence: SCHEMA‑Multitenancy (`SET search_path`) oder Hibernate‑`MultiTenantConnectionProvider`.
|
||||
- Registry: `control.tenants(event_id, schema_name, status, db_url?, version, created_at)`.
|
||||
|
||||
## Betroffene Bereiche
|
||||
|
||||
- Datenmodell tenant‑lokal: `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen`, `teilnehmer_konten`, `turnier_kassa` (siehe [Datenbankschema](./Schema/Database_Schema_V1-V009.md)).
|
||||
- Services: Der Entries‑Service arbeitet mandantenfähig; andere Services bleiben Single‑Tenant/global (vgl. Backend‑Roadmap).
|
||||
|
|
@ -11,9 +11,15 @@ Dieses Verzeichnis enthält die spezifische Dokumentation für alle Backend-Komp
|
|||
## Struktur
|
||||
|
||||
* `Services/`: Enthält pro Service eine dedizierte Markdown-Datei, die dessen Zweck, API, Datenmodell und Konfiguration beschreibt.
|
||||
* `API/`: Querliegende API-Referenzen und Übersichten (Stammdaten, Kassa, usw.).
|
||||
* `Schema/`: Datenbankschemata und Migrationsübersichten (Flyway).
|
||||
* `Integration/`: Dokumentation zur Interaktion zwischen den Services (z.B. Event-Flows).
|
||||
|
||||
## Wichtige Einstiegspunkte
|
||||
|
||||
* **[Ping-Service](./Services/PingService_Reference.md):** Dient als technischer Blueprint und einfachstes Beispiel für einen Service.
|
||||
* **[API-Gateway](../07_Infrastructure/api-gateway.md):** Beschreibung des zentralen Einstiegspunkts für alle externen Anfragen.
|
||||
* **[Stammdaten-APIs (Reiter, Pferde, Vereine, Funktionäre)](./API/API_Uebersicht_Stammdaten.md):** Konsolidierte Endpunkt-Übersicht (Backend B‑1 abgeschlossen).
|
||||
* **[Datenbankschema V1–V009](./Schema/Database_Schema_V1-V009.md):** Tabellen und Constraints (veranstaltungen, turniere, bewerbe, abteilungen, teilnehmer_konten, turnier_kassa).
|
||||
* **[Kassa-API](./API/Kassa_API.md):** Platzhalter für Saldo/Transaktionen; wird ergänzt, sobald Backend B‑2 Kassa fertig ist.
|
||||
* **[Tenant-Isolation & Multi‑Tenant kurz](./Multi_Tenant_Kurz.md):** Zusammenfassung gem. ADR‑0021.
|
||||
|
|
|
|||
130
docs/05_Backend/Schema/Database_Schema_V1-V009.md
Normal file
130
docs/05_Backend/Schema/Database_Schema_V1-V009.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Backend Developer
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
# Datenbankschema V1–V009 (Tenant‑Schema)
|
||||
|
||||
Quelle: Flyway‑Migrationen im Entries‑Service (`backend/services/entries/entries-service/src/main/resources/db/tenant/`), insbesondere `V2__domain_hierarchy.sql`.
|
||||
|
||||
Hinweis zur Architektur: Je Veranstaltung (Tenant) existiert ein eigenes Datenbankschema (ADR‑0021). Alle untenstehenden Tabellen werden pro Tenant‑Schema angelegt und sind somit mandantengetrennt.
|
||||
|
||||
## Tabellenübersicht
|
||||
|
||||
- `veranstaltungen` — Eine Veranstaltung (Singleton im Tenant‑Schema)
|
||||
- `turniere` — Turniere einer Veranstaltung (1:N zu `veranstaltungen`)
|
||||
- `bewerbe` — Bewerbe/Prüfungen eines Turniers (1:N zu `turniere`)
|
||||
- `abteilungen` — Abteilungen/Heats eines Bewerbs (1:N zu `bewerbe`)
|
||||
- `teilnehmer_konten` — Aggregierte Salden eines Teilnehmers über alle Turniere der Veranstaltung
|
||||
- `turnier_kassa` — Kassa‑Saldo pro Turnier
|
||||
|
||||
## Detaillierte Definitionen (aus V2__domain_hierarchy.sql)
|
||||
|
||||
### veranstaltungen
|
||||
Primärschlüssel: `id (UUID)`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
### turniere
|
||||
Primärschlüssel: `id (UUID)`
|
||||
Fremdschlüssel: `veranstaltung_id → veranstaltungen(id) ON DELETE CASCADE`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `veranstaltung_id UUID NOT NULL`
|
||||
- `oeps_turniernummer VARCHAR(50) NOT NULL`
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
Indizes/Constraints:
|
||||
- `UNIQUE (oeps_turniernummer)` → `uq_turniere_oeps_nr`
|
||||
- `INDEX (veranstaltung_id)` → `idx_turniere_veranstaltung_id`
|
||||
|
||||
### bewerbe
|
||||
Primärschlüssel: `id (UUID)`
|
||||
Fremdschlüssel: `turnier_id → turniere(id) ON DELETE CASCADE`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `turnier_id UUID NOT NULL`
|
||||
- `klasse VARCHAR(50) NOT NULL`
|
||||
- `hoehe_cm INTEGER NULL`
|
||||
- `bezeichnung TEXT NOT NULL`
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
Indizes:
|
||||
- `INDEX (turnier_id)` → `idx_bewerbe_turnier_id`
|
||||
- `INDEX (klasse)` → `idx_bewerbe_klasse`
|
||||
|
||||
### abteilungen
|
||||
Primärschlüssel: `id (UUID)`
|
||||
Fremdschlüssel: `bewerb_id → bewerbe(id) ON DELETE CASCADE`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `bewerb_id UUID NOT NULL`
|
||||
- `nr INTEGER NOT NULL`
|
||||
- `bezeichnung TEXT NOT NULL`
|
||||
- `typ VARCHAR(32) NOT NULL` (Werte: `SEPARATE_SIEGEREHRUNG`, `ORGANISATORISCH`)
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
Constraints/Indizes:
|
||||
- `CHECK (typ IN ('SEPARATE_SIEGEREHRUNG','ORGANISATORISCH'))` → `chk_abteilungen_typ`
|
||||
- `UNIQUE (bewerb_id, nr)` → `uq_abteilungen_bewerb_nr`
|
||||
- `INDEX (bewerb_id)` → `idx_abteilungen_bewerb_id`
|
||||
- `INDEX (typ)` → `idx_abteilungen_typ`
|
||||
|
||||
### teilnehmer_konten
|
||||
Primärschlüssel: `id (UUID)`
|
||||
Fremdschlüssel: `veranstaltung_id → veranstaltungen(id) ON DELETE CASCADE`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `veranstaltung_id UUID NOT NULL`
|
||||
- `teilnehmer_id UUID NOT NULL`
|
||||
- `saldo_cents BIGINT NOT NULL DEFAULT 0`
|
||||
- `currency CHAR(3) NOT NULL DEFAULT 'EUR'`
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
Indizes/Constraints:
|
||||
- `UNIQUE (veranstaltung_id, teilnehmer_id)` → `uq_tkonten_veranstaltung_teilnehmer`
|
||||
- `INDEX (veranstaltung_id)` → `idx_tkonten_veranstaltung_id`
|
||||
- `INDEX (teilnehmer_id)` → `idx_tkonten_teilnehmer_id`
|
||||
|
||||
### turnier_kassa
|
||||
Primärschlüssel: `id (UUID)`
|
||||
Fremdschlüssel: `turnier_id → turniere(id) ON DELETE CASCADE`
|
||||
|
||||
Spalten:
|
||||
- `id UUID PRIMARY KEY`
|
||||
- `turnier_id UUID NOT NULL`
|
||||
- `saldo_cents BIGINT NOT NULL DEFAULT 0`
|
||||
- `currency CHAR(3) NOT NULL DEFAULT 'EUR'`
|
||||
- `created_at TIMESTAMPTZ NOT NULL`
|
||||
- `updated_at TIMESTAMPTZ NOT NULL`
|
||||
|
||||
Indizes/Constraints:
|
||||
- `UNIQUE (turnier_id)` → `uq_turnier_kassa_turnier`
|
||||
- `INDEX (turnier_id)` → `idx_turnier_kassa_turnier_id`
|
||||
|
||||
## Versionierung / Flyway
|
||||
|
||||
- Die oben dokumentierten Tabellen sind in `V2__domain_hierarchy.sql` definiert.
|
||||
- Weitere Migrationen V1–V009 betreffen Bootstrap/Erweiterungen; diese Seite wird fortlaufend ergänzt, sobald neue fachrelevante Strukturen hinzukommen.
|
||||
|
||||
## Beziehungen (Kurz)
|
||||
|
||||
`veranstaltungen (1) ──< (N) turniere (1) ──< (N) bewerbe (1) ──< (N) abteilungen`
|
||||
|
||||
Separat aggregierend:
|
||||
|
||||
- `teilnehmer_konten` auf Veranstaltungsebene (pro Teilnehmer genau ein Konto)
|
||||
- `turnier_kassa` auf Turnierebene (pro Turnier genau ein Kassa‑Eintrag)
|
||||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Frontend Expert
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
### MVVM + UDF (Unidirectional Data Flow) — Referenz & Vorlage
|
||||
|
||||
Ziel: Alle ViewModels folgen einem klaren, einheitlichen Muster. Composables rendern nur `State` und senden `Intent`s. Business-Logik liegt im ViewModel, nicht in den UI-Funktionen.
|
||||
|
|
@ -14,7 +21,7 @@ Ziel: Alle ViewModels folgen einem klaren, einheitlichen Muster. Composables ren
|
|||
#### Referenz-Implementierung: Veranstalter
|
||||
|
||||
Dateien:
|
||||
- `frontend/features/veranstalter-feature/src/commonMain/.../VeranstalterViewModel.kt`
|
||||
- `frontend/features/veranstalter-feature/src/commonMain/kotlin/at/mocode/veranstalter/feature/presentation/VeranstalterViewModel.kt`
|
||||
- `frontend/features/veranstalter-feature/src/jvmMain/.../DefaultVeranstalterRepository.kt`
|
||||
- `frontend/features/veranstalter-feature/src/jvmMain/.../VeranstalterAuswahlScreen.kt` (verwendet das ViewModel)
|
||||
|
||||
|
|
@ -99,6 +106,10 @@ fun VeranstalterAuswahlScreen(onZurueck: () -> Unit, onWeiter: (Long) -> Unit) {
|
|||
- Persistenz/Netzwerk-Zugriffe laufen im Repository. Das ViewModel injiziert das Repository (später per DI).
|
||||
- State ist die Single Source of Truth pro Screen.
|
||||
|
||||
#### Siehe auch
|
||||
- Weitere Beispiele: `ReiterViewModel`, `PferdeViewModel`, `PingViewModel` in `frontend/features/*/presentation/`
|
||||
- Koin-Integration (VM-Erzeugung in Composables): `org.koin.compose.viewmodel.koinViewModel`
|
||||
|
||||
#### Vorlage für neue ViewModels
|
||||
1. `data class UiState(...)`
|
||||
2. `sealed interface Intent { ... }`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
type: Reference
|
||||
status: ACTIVE
|
||||
owner: Frontend Expert
|
||||
last_update: 2026-01-15
|
||||
last_update: 2026-04-03
|
||||
---
|
||||
|
||||
# Frontend-Architektur "Meldestelle Portal"
|
||||
|
|
@ -40,7 +40,9 @@ Das `frontend`-Verzeichnis ist wie folgt strukturiert, um eine klare Trennung de
|
|||
|
||||
## Wichtige Dokumente
|
||||
|
||||
* **[State-Management Strategie (UDF)](state-management-strategy.md):** Beschreibt die empfohlene Strategie für komplexe Screens.
|
||||
* **[MVVM + UDF Referenz (ViewModel-Architektur)](MVVM_UDF_Pattern.md):** Verbindliches Muster für ViewModels inkl. Referenz-Implementierung `VeranstalterViewModel`.
|
||||
* Referenz-Code: `frontend/features/veranstalter-feature/src/commonMain/kotlin/at/mocode/veranstalter/feature/presentation/VeranstalterViewModel.kt`
|
||||
* (Alt) **State-Management Strategie (UDF)**: Wird durch obige Referenz konsolidiert/ersetzt.
|
||||
* **[ADR-0010: SQLDelight für Cross-Platform-Persistenz](../01_Architecture/adr/0010-sqldelight-for-cross-platform-persistence.md):** Beschreibt die Entscheidung für SQLDelight.
|
||||
* **[ADR-0011: Koin für Dependency Injection](../01_Architecture/adr/0011-koin-for-dependency-injection.md):** Beschreibt die Entscheidung für Koin.
|
||||
* **[Offline-First-Architektur](offline-first-architecture.md):** Detaillierte Beschreibung der Offline-First-Strategie.
|
||||
|
|
|
|||
|
|
@ -42,3 +42,9 @@ Willkommen im "Gehirn" des Projekts. Dieses Verzeichnis ist die **Single Source
|
|||
* [Master Roadmap](01_Architecture/MASTER_ROADMAP.md)
|
||||
* [Agent Playbooks](04_Agents)
|
||||
* [Zora Infrastruktur](07_Infrastructure/Zora_System_Architektur.md)
|
||||
* [C-3 Proposal: Docs-Unterordner-Struktur](01_Architecture/Proposals/C-3_Docs-Strukturvorschlag.md)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Hinweis zur Reorganisation (C-3)
|
||||
Im Rahmen von C-3 wird eine Konsolidierung von Medien/Binärartefakten unter `docs/80_Assets/` sowie eine feinere Gliederung von Reports/Journals vorgeschlagen. Aktuell finden keine Verschiebungen statt – Details siehe Proposal oben.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user