114236c8d9
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
189 lines
5.1 KiB
Markdown
189 lines
5.1 KiB
Markdown
## 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. ✅
|