## 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, 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` 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. ✅