feat(MP-27): backend consolidation, gateway routing & service dockerfiles

Summary
- Backend Services (Entries, Results, Scheduling) haben Dockerfiles.
- Docker Compose Orchestrierung steht (DB + Gateway + Services).
- Gateway Routing für `entries-service` implementiert (StripPrefix, Path Rewrites).
- Health-Checks und 409-Conflict-Demo Endpunkt verifiziert.

Verification
- `docker compose up --build` -> Success
- `curl http://localhost:8081/api/entries` -> 200 OK (routed through Gateway)

Ref: MP-27
This commit is contained in:
2025-12-08 11:39:43 +01:00
parent b4769d89bc
commit 114236c8d9
12 changed files with 368 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
## Frontend-Architektur
# Frontend-Architektur
**Architektur-Stil:** Feature-First, Clean Architecture, Kotlin Multiplatform (KMP)
## 1. Management Summary
Das Frontend wird als **modulare Kotlin Multiplatform (KMP) Anwendung** entwickelt. Ziel ist eine strikte Trennung von
technischer Basis (`shared`) und fachlichen Funktionen (`features`). Die Architektur ist **"Offline-Ready"** konzipiert:
Durch die konsequente Nutzung des Repository-Patterns kann die Datenquelle später transparent von "Online-Only" (API)
auf "Local-First" (Datenbank + Sync) umgestellt werden, ohne die Benutzeroberfläche (UI) anpassen zu müssen.
## 2. Die Modul-Struktur (Gradle)
Die Anwendung ist nicht monolithisch, sondern in fachliche Module geschnitten ("Feature-First").
- **`:clients:app` (Der Container)**
- **Rolle:** Der Einstiegspunkt ("Main").
- **Aufgabe:**
Initialisierung von Koin (Dependency Injection).
Globales Theming (Material3).
High-Level Navigation (Routing zwischen Features).
Verbindet alle Feature-Module.
- **`:clients:shared` (Das Fundament)**
- **Rolle:** Die gemeinsame Bibliothek für alle Features.
- **Inhalt:**
**DI (Dependency Injection):** Zentrale Koin-Module (NetworkModule, CoreModule).
**Network:** Konfigurierter HttpClient (Singleton).
**Domain Core:** Basis-Modelle (Resource<T>, ApiError, User).
**UI Kit:** Gemeinsame Komponenten (LoadingSpinner, ErrorView), Typography, Colors.
- **Regel:** Hier liegt keine Fachlogik eines spezifischen Features (keine Pferdedaten, keine Nennungen).
- **`:clients:*-feature` (Die Fachlichkeit)**
- **Beispiele:** `:clients:auth-feature`, `:clients:registry-feature`, `:clients:events-feature`.
- **Rolle:** Kapselt einen kompletten fachlichen Bereich.
- **Inhalt:** Eigene Screens, ViewModels und spezifische Use-Cases.
## 3. Die Schichten-Architektur (Innerhalb eines Moduls)
Jedes Modul (besonders `shared` und die Features) folgt einer strikten 3-Schichten-Architektur. Das garantiert
Testbarkeit und Austauschbarkeit.
### 1️⃣ Presentation Layer (UI)
- **Technologie:** Compose Multiplatform.
- **Komponenten:** `Screen` (Composable Functions) und `ViewModel` (State-Holder).
- **Verantwortung:**
Zeigt Daten an (Reagiert auf State).
Nimmt User-Input entgegen.
**Weiß NICHT**, woher die Daten kommen (Netzwerk oder DB).
Nutzt Repositories via Koin Injection (`koinInject()`).
### 2️⃣ Domain Layer (Die Logik)
- **Komponenten:** `Repository Interface`, `Models`.
- **Verantwortung:**
Definiert **WAS** getan werden kann (z.B. `checkSystemStatus()`).
Ist komplett unabhängig von Frameworks (reines Kotlin).
Nutzt `Resource<T>` Wrapper (`Success`, `Error`, `Loading`) für den State-Transport.
### 3️⃣ Data Layer (Die Umsetzung)
- **Komponenten:** `Repository Implementation`, `API-DTOs`, `Database-Entities`.
- **Verantwortung:**
Entscheidet **WIE** Daten geholt werden.
**Aktuell (Online):** Ruft Ktor Client auf.
**Zukunft (Offline/LAN):** Prüft lokale SQLite DB, synchronisiert im Hintergrund.
Mappt rohe API-Daten (DTOs) in saubere Domain-Modelle.
### 4. Technische Kern-Konzepte
**💉 Dependency Injection (Koin)**
Wir nutzen Koin als "Klebstoff".
- Module exportieren ihre Funktionalität via `val myModule = module { single { ... } }`.
- Die `clients:app` sammelt alle Module ein und startet den Container.
- **Vorteil:** Repositories und der HTTP-Client müssen nicht manuell herumgereicht werden.
**🌐 Networking (Ktor)**
Ein zentraler `HttpClient` im `NetworkModule` (`clients:shared`).
- Konfiguriert mit JSON-Serialization.
- Zentrale Base-URL Steuerung (via `AppConfig` umschaltbar für LAN/Dev/Prod).
- Timeout- und Logging-Handling an einer Stelle.
**🔄 Datenfluss (Unidirectional Data Flow)**
1. **UI:** Trigger Event (Button Click).
2. **ViewModel/Scope:** Ruft Repository auf (`launch { repo.getData() }`).
3. **Repository:** Liefert `Resource.Loading` -> `Resource.Success(data)`.
4. **UI:** Rendert den neuen State.
### 5. Strategie für Offline & LAN-Fähigkeit (Zukunft)
Die aktuelle Architektur ist **vorbereitet** für die Offline-Anforderungen der großen Turniere.
**Die Transition (Schritt-für-Schritt):**
1. **Phase 1 (Jetzt):** PingRepositoryImpl ruft direkt httpClient.get() auf.
2. **Phase 2 (Offline):**
Wir fügen **SQLDelight** (lokale DB) hinzu.
Die `PingRepositoryImpl` wird geändert (ohne dass die UI es merkt!):
```Kotlin
fun getData() {
val localData = db.dao.getAll()
if (localData.isEmpty()) {
val remote = api.getAll()
db.dao.insert(remote)
return remote
}
return localData
}
```
3. **Phase 3 (Sync):** Ein Hintergrund-Worker (`WorkManager` oder `Coroutine`) synchronisiert lokale Änderungen mit dem
Server, sobald das LAN verfügbar ist.
### 6. Zusammenfassung für Entwickler
Wenn du ein neues Feature (z.B. "Pferde anzeigen") baust:
1. **Domain:** Erstelle Horse Model und HorseRepository Interface.
2. **Data:** Erstelle HorseRepositoryImpl. Injiziere den HttpClient.
3. **DI:** Registriere das Repository im Koin-Modul.
4. **UI:** Erstelle HorseListScreen. Injiziere das Repository.
5. Fertig.
Diese Struktur ist sauber, skalierbar und bereit für den professionellen Einsatz beim OEPS. ✅
+14
View File
@@ -0,0 +1,14 @@
## Epic 1: BigBang vorbereiten Snapshot und Branching
* Zusammenfassung: Snapshot des aktuellen Zustands, Tag setzen, BigBangBranch erstellen
* Beschreibung:
* Ziel: Den aktuellen Projektstand einfrieren, um die anschließende große Umstrukturierung nachvollziehbar zu machen.
* Schritte:
1. PR „chore: snapshot prerefactor state“ auf `main` erstellen und mergen.
2. GitTag `v0-pre-refactor` setzen.
3. Branch `refactor/big-bang-architecture` anlegen und als Arbeitsbasis verwenden.
* Artefakte:
* PRLink, TagNachweis.
* Definition of Done (DoD):
* PR gemergt, Tag `v0-pre-refactor` existiert (git log / remote tags sichtbar).
* Branch `refactor/big-bang-architecture` öffentlich verfügbar.
+27
View File
@@ -0,0 +1,27 @@
## Epic 2: RepositoryStruktur auf MeldestellenDomäne umstellen
* Zusammenfassung: Neues Skeleton und physisches Umziehen der Module/Ordner
* Beschreibung:
* Ziel: Einführen der Zielstruktur gemäß Domäne (Meldestelle, ÖTO/FEI) für Frontend, Backend und Docker.
* SollStruktur (TopLevel):
* `frontend/` (shells, features, core)
* `backend/` (gateway, discovery, services)
* `docker/` (docker-compose.yml, .env.example)
* `docs/` (adr, ARCHITECTURE.md)
* Geplanter Umzug:
* Frontend:
* `clients/app``frontend/shells/meldestelle-portal`
* `clients/shared/common-ui``frontend/core/design-system`
* `clients/shared/navigation``frontend/core/navigation`
* Backend:
* `services|domains|infrastructure|platform|core` → konsolidieren unter `backend/`:
* `backend/services/*`
* `backend/gateway`, `backend/discovery`
* Docker:
* `compose.yaml``docker/docker-compose.yml`
* Pro Service ein eigener `Dockerfile` im jeweiligen ServiceOrdner
* Definition of Done (DoD):
* VerzeichnisSkeleton vorhanden und committed.
* Dateien physisch verschoben; alle relativen Pfade in Gradle/Konfigurationen angepasst (oder bekannte Fehlerliste
dokumentiert).
* `docker/docker-compose.yml` vorhanden; `.env.example` erstellt.
+14
View File
@@ -0,0 +1,14 @@
## Epic 3: Gradle/Build Governance zentralisieren
* Zusammenfassung: Version Catalog, Settings, BuildKonventionen
* Beschreibung:
* Ziel: Einheitliche BuildBasis für alle Module (KMP/JVM), zentrale Versionierung und klare ProjektIncludes.
* Schritte:
1. `gradle/libs.versions.toml` als Single Source of Truth etablieren.
2. `settings.gradle.kts` an neue Struktur anpassen (Includes für FrontendCore, Features, Shells;
BackendServices).
3. BuildKonventionen konsolidieren (`buildSrc` oder `gradle/plugins`): Kotlin, Compose, Detekt, KTLint.
* Definition of Done (DoD):
* `./gradlew projects` zeigt die neue ModulHierarchie ohne verwaiste Einträge.
* Alle Subprojekte beziehen Versionen aus `libs.versions.toml` (keine harten Versionsnummern in Buildskripten).
* Lint/Detekt laufen in CI lokal erfolgreich.
+21
View File
@@ -0,0 +1,21 @@
## Epic 4: Frontend Core etablieren (Network, LocalDB, DesignSystem, Domain)
* Zusammenfassung: Koin/KtorClient, SQLDelight, DesignSystemKonsolidierung, Shared Domain Models
* Beschreibung:
* Ziel: Eine stabile, wiederverwendbare Basis für alle Features/Shells schaffen.
* Deliverables:
* `frontend/core/network`:
* Ktor `HttpClient` mit Plugins: Auth, Retry, JSON, Timeouts, Logging.
* KoinModule mit `single(named("apiClient"))` Export.
* `frontend/core/local-db`:
* SQLDelightSetup (KMP), SchemaOrdner, `1.sqm` Migration, BuildKonfig.
* Konvention: Keine InlineSQL in BusinessCode; Nutzung der generierten Queries.
* `frontend/core/design-system`:
* Konsolidierte `AppTheme`, Komponenten, Tokens.
* `frontend/core/domain`:
* Gemeinsame Modelle/IDs (z. B. `Turnier`, `Pruefung`, `Start`, `ReiterId`, `PferdId`, `Lizenz` …).
* Definition of Done (DoD):
* Kein Vorkommen manueller `Authorization`Header im Code (nur DI`apiClient`).
* SQLDelight generiert Code; eine DemoQuery kompiliert für das WebTarget.
* DesignSystem kompiliert; `AppTheme` nutzbar in Shell/FeatureSample.
* `core/domain` wird von Features konsumiert, ohne Feature→Feature Abhängigkeiten.
+17
View File
@@ -0,0 +1,17 @@
## Epic 5: FeaturePilot NennungsManagement (Vertical Slice)
- Zusammenfassung: Erstes Feature in Zielstruktur mit Route, UI, Data, Domain, DI
- Beschreibung:
- Ziel: Funktionsfähiger, isolierter Slice für Nennungen (Turnierbezogen), inkl. Offline/SyncPfad.
- Struktur: `frontend/features/nennungs-management/{api,ui,data,domain,di}`
- Funktionen (Minimum):
- Liste Nennungen für `turnierId` anzeigen (UI liest aus LocalDB; initialer Sync).
- Anlegen/Ändern einer Nennung lokal; Sync zum Server.
- Konfliktpfad: Bei veralteter `version` → Server 409; UI Toast + automatischer Refresh.
- Navigation/Route:
- `meldestelle/nennungen/turnier/{turnierId}` als EntryPoint.
- Definition of Done (DoD):
- Shell `meldestelle-portal` kann Route aufrufen; UI rendert Daten aus LocalDB.
- APICall nutzt DI`apiClient`.
- `lastSyncedAt` steuert FreshnessIndikator (grün/gelb/rot) im UI.
- Konfliktfall 409 nachweisbar (Mock/Backend) und UI reagiert gemäß Guideline.
+13
View File
@@ -0,0 +1,13 @@
## Epic 6: Shell MeldestellePortal (JS/Wasm)
- Zusammenfassung: Exekutierbare WebShell mit expliziten FeatureDependencies
- Beschreibung:
- Ziel: Eine Shell, die nur benötigte Features bundelt (TreeShaking wirksam) und die Navigation kapselt.
- Schritte:
1. Entry Point/Bootstrap der Shell anlegen (`frontend/shells/meldestelle-portal`).
2. Nur benötigte Features als GradleDependencies einbinden (Pilot: `nennungs-management`).
3. BundleReport aktivieren; CodeSplitting konfigurieren.
- Definition of Done (DoD):
- `./gradlew :frontend:shells:meldestelle-portal:build` erfolgreich.
- BundleReport zeigt, dass ausschließlich integrierte Features im Output sind.
- Navigation aus der Shell zu FeatureRouten funktioniert.
+18
View File
@@ -0,0 +1,18 @@
## Epic 7: BackendKonsolidierung Services & Gateway
- Zusammenfassung: Services domänenspezifisch schneiden, Gateway verankern, Compose lauffähig
- Beschreibung:
- Ziel: Minimales, aber kohärentes Backend gemäß Zielstruktur startfähig via Docker Compose.
- Services (erste Iteration):
- `entries-service` (Nennungen/Validierung),
- `results-service` (Ergebnisse),
- `scheduling-service` (Zeit/Abteilungen),
- plus `gateway` (Auth/Routing).
- Anforderungen:
- Pro Service eigener `Dockerfile` im ServiceOrdner.
- Mindestens ein Endpunkt pro Service (Stub akzeptabel), 409Konfliktpfad im `entries-service`.
- Compose: DB + Gateway + `entries-service` startbar.
- Definition of Done (DoD):
- `docker/docker-compose.yml` gestartet → Services erreichbar (Health/HTTP 200/StubDaten).
- Endpoint für 409Konflikt getestet (z. B. Postman/HTTPTest dokumentiert).
- Gateway leitet Requests korrekt weiter.
+12
View File
@@ -0,0 +1,12 @@
## Epic 8: ArchitekturGuards und CI Regeln durchsetzen
- Zusammenfassung: Detekt/KTLint Regeln, Dependency Checks, Bundle Budgets
- Beschreibung:
- Ziel: Die Architekturregeln technisch erzwingen, um Regressionen zu vermeiden.
- Guards:
- Verbot Feature→Feature Dependencies (nur → `frontend/core:*`).
- Verbot manueller `Authorization`Header im Code (nur DI`apiClient`).
- CI: DependencyGraphDiff, BundleSizeBudget je Shell, `docker compose config` Validierung.
- Definition of Done (DoD):
- CI bricht bei Verstößen, Berichte werden erzeugt.
- Auf `main` grüner Build mit aktiven Guards.
+13
View File
@@ -0,0 +1,13 @@
## Epic 9: Dokumentation & ADRs formalisieren
- Zusammenfassung: ADRTemplate, Entscheidungen festschreiben, Architecture Guide aktualisieren
- Beschreibung:
- Ziel: Entscheidungen nachvollziehbar machen und Onboarding erleichtern.
- Schritte:
1. ADRTemplate `docs/adr/ADR-000-template.md` anlegen.
2. ADRs erstellen für: Koin, SQLDelight, Optimistic Locking, FreshnessUI, CoreDomain, FeatureIsolation über
Routen.
3. `docs/ARCHITECTURE.md`: Mapping Vision ↔ Repo, Rollen, Routenbeispiele (Meldestelle, Richter, Zeitnahme).
- Definition of Done (DoD):
- Mindestens 5 ADRs vorhanden und verlinkt.
- `ARCHITECTURE.md` erlaubt Onboarding in < 30 Minuten (interner Review bestanden).
+6
View File
@@ -0,0 +1,6 @@
## Ergänzende Hinweise (Optionen)
- Labels/Tags: `architecture`, `big-bang`, `frontend`, `backend`, `docker`, `docs`.
- Priorisierung: 1 → 9 in ungefährer Reihenfolge, wobei 24 parallelisierbar sind.
- Zeitbox für Epics definieren (z. B. 12 Wochen je Epic in früher Phase) und nach jedem Epic Zwischenrelease (Tag)
setzen.