Projekt: Online Novel Lib Komponente: Frontend (Kotlin Multiplatform) Stand: 23.09.2026
Das Frontend ist die Anwendung, mit der Leser Novels entdecken und kapitelweise lesen, Autoren eigene Novels und Kapitel verfassen und verwalten, und Admins Nutzer sowie Inhalte moderieren. Es wird mit Kotlin Multiplatform (Compose Multiplatform als UI-Framework) gebaut, sodass möglichst viel Logik und UI-Code zwischen den Plattformen geteilt wird. Es spricht ausschließlich über die REST-API mit dem in einem separaten Dokument beschriebenen Spring-Boot-Backend.
Für die erste Ausbaustufe werden Android, iOS und Web (Compose Multiplatform for Web, Kotlin/Wasm) unterstützt. Ein natives Desktop-Target (Windows/Mac/Linux) ist bewusst nicht im Scope: Das Web-Target deckt denselben Zweck – ein schneller, emulatorloser Blick auf den aktuellen Stand im Browser – ab, ohne einen separaten nativen Build-/Verteilungsweg zu benötigen. Die Architektur (siehe Abschnitt 4) wird so gewählt, dass ein Desktop-Target später ohne größere Umbauten ergänzt werden könnte, indem lediglich ein neues Plattform-Modul (jvmMain/desktopApp) mit den entsprechenden plattformspezifischen Implementierungen hinzukommt.
Android ist die primäre Zielplattform. Da für die Entwicklung aktuell kein Mac zur Verfügung steht, ist Android die einzige Plattform, auf der neue Funktionen laufend real (Emulator/Testgerät) verifiziert werden; das Web-Target dient als sekundäre, ebenfalls sofort lauffähige Möglichkeit für einen schnellen Blick im Browser. iOS-Code wird von Anfang an im gemeinsamen commonMain mitgepflegt und muss kompilieren (compileIosMainKotlinMetadata), bleibt aber bis zur Verfügbarkeit eines Mac mit Xcode zur Laufzeit unverifiziert – geplante Funktionsumfänge dürfen nicht implizit von einer bereits erfolgten iOS-Laufzeitprüfung ausgehen. Sobald ein Mac verfügbar ist, sollten die bis dahin aufgelaufenen Iterationen einmal gebündelt auf dem iOS-Simulator nachverifiziert werden.
| Bereich | Wahl | Anmerkung |
|---|---|---|
| UI-Framework | Compose Multiplatform | Gemeinsamer UI-Code für Android, iOS, Web |
| Sprache | Kotlin | |
| Netzwerk | Ktor Client | Multiplatform-HTTP-Client für die REST-Kommunikation mit dem Backend |
| Serialisierung | kotlinx.serialization | JSON (de)serialisieren |
| API-Client | openapi-generator (kotlin, library=multiplatform) |
Erzeugt den Ktor-Client samt Modellen aus dem OpenAPI-Vertrag des Backends – siehe Abschnitt 10.1 |
| Datum/Zeit | kotlinx-datetime | Von den generierten Modellen für date-time-Felder verwendet |
| Dependency Injection | Koin | Gut etabliert im KMP-Umfeld, einfache Einbindung |
| Navigation | Compose Multiplatform Navigation (oder alternativ Voyager) | Plattformübergreifendes Screen-Routing |
| Lokale Datenhaltung/Cache | SQLDelight | Multiplatform-SQL für lokales Zwischenspeichern gelesener Kapitel und ausstehender Fortschritts-Updates |
| Sicherer Token-Speicher | multiplatform-settings (mit verschlüsseltem Backend je Plattform, wo verfügbar) | Android: EncryptedSharedPreferences/DataStore; iOS: Keychain; Web: localStorage (kein OS-Schlüsselbund im Browser verfügbar – siehe Sicherheitshinweis in Abschnitt 10) |
| Nebenläufigkeit | Kotlin Coroutines & Flow |
Das Projekt folgt dem üblichen KMP-Modulaufbau: commonMain enthält die gesamte gemeinsame Logik – Datenmodelle, Netzwerk-Client, ViewModels/State-Holder, UI-Screens und -Komponenten in Compose Multiplatform. Plattformspezifische Module (androidMain, iosMain, wasmJsMain) enthalten ausschließlich das, was sich nicht teilen lässt: z. B. die konkrete Implementierung des sicheren Token-Speichers, Plattform-Einstiegspunkte (Activity unter Android, Browser-Entry-Point unter Web, entsprechendes iOS-Setup) sowie ggf. plattformspezifischen Dateizugriff für Cover-Bilder. Ziel ist ein möglichst hoher Anteil an commonMain-Code, damit neue Features nur einmal implementiert werden müssen.
Analog zum Backend (siehe Backend-Anforderungen, Abschnitt 3) prüft das Frontend nicht auf feste Rollennamen, sondern auf einzelne Permissions, die nach dem Login aus dem JWT bzw. per GET /user/me geladen werden. Das hat den Vorteil, dass eine später hinzukommende Rolle mit nur einer Teilmenge der Admin-Rechte (z. B. „Moderator”, der nur die Meldungs-Warteschlange sehen darf) automatisch die passenden UI-Teile freischaltet, ohne dass die App-Logik geändert werden muss:
NOVEL_CREATE, wird zusätzlich ein „Meine Novels”-Bereich mit Autoren-Dashboard und Kapitel-Editor angezeigt. Fehlt sie, sieht der Nutzer stattdessen einen Einstiegspunkt „Autor werden” (Backend-Endpunkt POST /user/me/role-requests, siehe Backend-Anforderungen Abschnitt 4.8 und iterationplan-backend.md, Iteration 4.5; der Antrag wird aktuell automatisch genehmigt und vergibt intern die Rolle AUTHOR mit NOVEL_CREATE).USER_MANAGE schaltet die Nutzerverwaltung frei, ROLE_MANAGE die Möglichkeit, Rollen zuzuweisen, REPORT_REVIEW die Moderationswarteschlange, NOVEL_EDIT_ANY/CHAPTER_EDIT_ANY das Entfernen fremder Novels/Kapitel. Ein Nutzer mit nur REPORT_REVIEW sieht so ausschließlich die Meldungs-Warteschlange und keinen vollen Admin-Bereich.Authentifizierung: Registrierung (E-Mail, Benutzername, Passwort), Login, Logout, automatisches Halten der Session über gespeicherte Tokens, automatisches Erneuern des Access Tokens über den Refresh Token im Hintergrund. Ein „Passwort vergessen”-Flow sowie Social Login sind bewusst nicht Teil der ersten Ausbaustufe.
Katalog/Entdecken: Liste aller veröffentlichten Novels mit Cover, Titel, Kurzbeschreibung, Autor(en), Status, Genre-Tags und Altersfreigabe-Badge (ALL_AGES/TEEN/MATURE); Suche nach Titel; Filterung nach Genre/Status/Altersfreigabe; Paginierung bzw. „mehr laden” beim Scrollen.
Novel-Detailansicht: Vollständige Beschreibung, Anzeige aller zugeordneten Autoren, Altersfreigabe und optionale Inhaltshinweise (Content Warnings), Liste der veröffentlichten Kapitel (mit Nummer und Titel), Button „Zur Bibliothek hinzufügen” bzw. Statusanzeige, falls das Novel bereits in der eigenen Bibliothek ist, ein „Weiterlesen”-Button, der direkt zur zuletzt gelesenen Position springt, sowie ein „Melden”-Button für unangemessene Inhalte oder Urheberrechtsverletzungen (siehe Reporting weiter unten).
Eigene Bibliothek: Liste aller Novels, die der Nutzer seiner Bibliothek hinzugefügt hat, gruppiert oder filterbar nach Lesestatus (liest gerade, abgeschlossen, geplant, abgebrochen), mit Fortschrittsanzeige (z. B. „Kapitel 12 von 30” oder Prozentanzeige) pro Eintrag. Ein DRAFT-Novel kann hier nie erscheinen, auch nicht das eigene (siehe Backend-Anforderungen, Abschnitt 4.5) – ein Autor, der seine unveröffentlichten Novels sehen will, nutzt dafür das Autoren-Dashboard, nicht die Bibliothek.
Leseansicht (Reader): Kapiteltext in gut lesbarer Darstellung, Navigation zum vorherigen/nächsten Kapitel, Einstellungen für Schriftgröße und Farbschema (hell/dunkel/sepia), automatische Fortschrittsspeicherung (siehe Abschnitt 7), sowie eine Melden-Option für das aktuelle Kapitel. Kapitel tragen serverseitig bereits ein accessLevel-Feld (FREE/PREMIUM); die App zeigt aktuell noch keine Kauf-/Bezahllogik an, sollte aber ein PREMIUM-Kapitel bereits optisch kennzeichnen können (z. B. Schloss-Icon), damit eine spätere Einführung kostenpflichtiger Kapitel ohne UI-Konzeptänderung möglich ist.
Autoren-Dashboard: Übersicht der eigenen Novels (inkl. Novels, bei denen man als Co-Autor hinzugefügt wurde), geladen über GET /user/me/novels – dies ist zugleich die einzige Ansicht, in der ein Autor seine eigenen DRAFT-Novels sieht, da die Bibliothek sie kategorisch ausschließt (siehe oben). Mit Möglichkeit, ein neues Novel anzulegen (Titel, Beschreibung, Cover, Genre, Status, Altersfreigabe, optionale Inhaltshinweise) sowie bestehende zu bearbeiten oder zu löschen; pro Novel eine Kapitelliste, die sich wie eine frei sortierbare Liste verhält – ein Kapitel wird per Drag & Drop an eine beliebige Position gezogen, das Frontend meldet dabei nur, hinter welchem Nachbar-Kapitel es jetzt stehen soll (siehe Backend-Anforderungen, PUT /novels/{novelId}/chapters/{chapterId}/position), die angezeigte Kapitelnummer kommt danach automatisch neu vom Server und muss clientseitig nicht selbst berechnet werden; außerdem können Kapitel angelegt, als Entwurf gespeichert oder veröffentlicht werden; zusätzlich eine Verwaltung der zugeordneten Autoren (Liste, weiteren Autor per Benutzername/E-Mail hinzufügen, Autor entfernen).
Kapitel-Editor: Eingebauter Text-Editor (Markdown oder einfacher Rich-Text, der intern als Markdown gespeichert wird) zum Schreiben und Bearbeiten von Kapiteln, mit Titel-Feld, Speichern-als-Entwurf- und Veröffentlichen-Aktion sowie einer Live- oder Vorschau-Ansicht, wie das Kapitel später für Leser aussieht.
Admin-Bereich: In einzelne, permission-gesteuerte Teilbereiche gegliedert (siehe Abschnitt 5): Nutzerliste mit Möglichkeit, Accounts zu sperren (USER_MANAGE) bzw. Rollen zuzuweisen/zu entziehen (ROLE_MANAGE); Übersicht/Suche über alle Novels mit Möglichkeit, einzelne Novels oder Kapitel zu entfernen (NOVEL_EDIT_ANY/CHAPTER_EDIT_ANY); eine Moderationswarteschlange (REPORT_REVIEW), die offene Meldungen (Reports) aus Abschnitt „Melden” auflistet und pro Meldung erlaubt, sie abzulehnen oder eine Aktion (Inhalt/Nutzer sperren) zu vermerken.
Melden (Reporting): Ein einfaches Formular (Grund aus einer festen Auswahl wie Urheberrecht, unangemessener Inhalt, Spam, Sonstiges, plus optionaler Freitext), erreichbar über den „Melden”-Button auf Novel-Detailseite, in der Leseansicht (bezogen auf das aktuelle Kapitel) und auf Autoren-/Nutzerprofilen. Nach dem Absenden erhält der Nutzer eine Bestätigung; der weitere Bearbeitungsstatus wird nicht an den meldenden Nutzer zurückgemeldet, sondern landet in der Admin-Moderationswarteschlange.
Einstellungen/Profil: Anzeigename ändern, Passwort ändern, Logout, App-weites Farbschema.
Damit ein Nutzer auf einem anderen Gerät oder Browser genau dort weiterlesen kann, wo er aufgehört hat, speichert das Frontend den Fortschritt nicht nur lokal, sondern sendet ihn an das Backend (PUT /user/me/library/{novelId}/progress, siehe Backend-Anforderungen). Ein Update wird ausgelöst, wenn der Nutzer ein Kapitel verlässt, die App/den Screen verlässt oder in regelmäßigen Abständen während des Lesens (z. B. alle 30 Sekunden bei aktivem Scrollen). Beim Öffnen eines Novels wird zunächst der serverseitig gespeicherte Fortschritt geladen, um die Leseansicht an der richtigen Stelle zu öffnen. Ist das Gerät offline, wird das Update lokal (SQLDelight) zwischengespeichert und beim nächsten Verbindungsaufbau nachgereicht.
Bereits geladene Kapitel werden lokal zwischengespeichert (SQLDelight), damit ein erneutes Öffnen ohne Netzwerkzugriff funktioniert und die App insgesamt reaktionsschneller wirkt. Ausstehende, noch nicht an das Backend übermittelte Fortschritts-Updates werden ebenfalls lokal vorgehalten und bei Konnektivität synchronisiert. Ein vollständiger Offline-Download ganzer Novels ist für die erste Ausbaustufe nicht vorgesehen (siehe Abschnitt 14).
Jeder Screen besitzt einen State-Holder/ViewModel in commonMain, der über Kotlin StateFlow einen unidirektionalen Datenfluss (State nach außen, Events/Intents nach innen) umsetzt. Dadurch bleibt die UI-Schicht (Compose) weitgehend zustandslos und dieselbe Logik kann auf allen Plattformen wiederverwendet werden.
Der Ktor-Client wird pro Umgebung (lokal/später Produktion) mit einer konfigurierbaren Basis-URL initialisiert; für die lokale Entwicklung ergeben sich je nach Plattform unterschiedliche Adressen für das lokal laufende Backend (z. B. http://localhost:8080 für Web/iOS-Simulator, http://10.0.2.2:8080 für den Android-Emulator). Da Web-Dev-Server und Backend auf unterschiedlichen Ports (= unterschiedlichen Origins) laufen, muss das Backend für lokale Entwicklung CORS-Header für die Origin des Web-Dev-Servers senden, sonst blockt die Same-Origin-Policy des Browsers alle Requests clientseitig, bevor der Auth-Interceptor überhaupt greift. Ein Auth-Interceptor fügt automatisch den Access Token an ausgehende Requests an und erneuert ihn bei Ablauf transparent über den Refresh Token; schlägt auch das fehl, wird der Nutzer zum Login zurückgeführt. Netzwerkfehler werden einheitlich behandelt und dem Nutzer verständlich angezeigt (z. B. „Keine Verbindung zum Server”). Da das Backend produktiv hochverfügbar mit mehreren Instanzen hinter einem Load Balancer betrieben wird (siehe Backend-Anforderungen), können bei Deployments vereinzelt kurzzeitige Fehler auftreten; für Requests wird daher eine einfache automatische Wiederholung bei transienten Fehlern (z. B. HTTP 502/503 oder Verbindungsabbruch) mit ein bis zwei Versuchen vorgesehen, bevor dem Nutzer ein Fehler angezeigt wird.
Der API-Client wird nicht von Hand geschrieben, sondern aus dem OpenAPI-Vertrag des Backends generiert (api/openapi.yaml im Backend-Repository, siehe Backend-Anforderungen, Abschnitt 5.1). Das Backend generiert aus derselben Datei seine Server-Interfaces, sodass Client und Server nicht auseinanderlaufen können: Feldnamen, Pflichtfelder, Enum-Werte und Statuscodes stammen auf beiden Seiten aus einer einzigen Quelle.
Konkret bedeutet das für dieses Projekt:
kotlin, library=multiplatform) zu einem Ktor-Client mit kotlinx.serialization-Modellen in commonMain erzeugt. Die Option serializationLibrary darf dabei nicht zusätzlich gesetzt werden – die Multiplatform-Bibliothek bringt kotlinx.serialization bereits mit, und beides zusammen erzeugt eine doppelte @Serializable-Annotation, die nicht kompiliert.ProblemDetail mit seinem stabilen code-Feld ist Teil des Vertrags. Die Fehlerbehandlung der App wertet code aus statt der für Menschen gedachten Meldung, und muss unbekannte Werte tolerieren, da neue Codes additiv ergänzt werden dürfen.Empfohlen wird ein Material-3-basiertes Design über Compose Multiplatform als konsistente Basis auf allen Plattformen, mit Anpassungen für größere Bildschirme im Web-Browser (z. B. mehrspaltige Katalogansicht, breitere Leseansicht mit begrenzter Textbreite für Lesbarkeit) gegenüber der kompakteren mobilen Darstellung. Eine detaillierte visuelle Gestaltung (Farbschema, Typografie) wird in einem separaten Design-Schritt festgelegt und ist nicht Gegenstand dieses Anforderungsdokuments.
Lange Kapiteltexte werden performant dargestellt (Lazy Loading langer Textabschnitte statt Rendern des gesamten Kapitels auf einmal, falls nötig); die Schriftgröße ist skalierbar, um unterschiedlichen Lesebedürfnissen gerecht zu werden; gemeinsame Logik in commonMain wird mit Unit-Tests abgedeckt, plattformspezifische Teile nach Bedarf ergänzend mit UI-Tests.
Die Entwicklung erfolgt in Android Studio mit installiertem Kotlin-Multiplatform-Plugin. Für Android wird ein Emulator oder ein Testgerät benötigt; da aktuell kein Mac zur Verfügung steht, ist Android die primäre Plattform für die tägliche Entwicklung und Verifikation. Das Web-Target lässt sich direkt über die Gradle-Aufgabe wasmJsBrowserDevelopmentRun im Browser starten (öffnet automatisch einen lokalen Dev-Server) und eignet sich als sekundäre Möglichkeit für schnelle Entwicklungszyklen ohne Emulator. Für das iOS-Target werden zusätzlich Xcode und ein Mac vorausgesetzt; bis ein Mac verfügbar ist, beschränkt sich die Verifikation auf das Kompilieren des gemeinsamen Codes (./gradlew :shared:compileIosMainKotlinMetadata), ohne Laufzeittest auf einem Simulator/Gerät. In allen Fällen zeigt die App im lokalen Entwicklungsmodus auf das lokal laufende Spring-Boot-Backend (siehe Backend-Anforderungen, Abschnitt „Lokales Setup”) – für das Web-Target muss dessen CORS-Konfiguration die Origin des Dev-Servers erlauben (siehe Abschnitt 10).
Folgende Punkte sind bewusst nicht Teil der ersten Ausbaustufe: natives Desktop-Target (Windows/Mac/Linux) – ursprünglich vorgesehen, zugunsten des Web-Targets (schnellerer Blick ins UI ohne Emulator, kein separates natives Artefakt) aus dem Scope genommen, siehe Abschnitt 2; OAuth-/Social-Login-UI (sobald backendseitig verfügbar); vollständiger Offline-Download ganzer Novels statt nur einzelner bereits gelesener Kapitel; Kommentare und Bewertungen zu Novels/Kapiteln in der UI; Push-Benachrichtigungen bei neuen Kapiteln abonnierter Novels; „Passwort vergessen”-Flow.