tooling: make .junie/.gemini docs-first (remove legacy guidelines)
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
# Strategische Analyse der Build-Tooling-Integrität: Interoperabilität von Kotlin 2.3.0, Java 25 und KSP in Spring Boot 3.5.9 Architekturen
|
||||
|
||||
## Einleitung und Kontextualisierung des Ökosystems 2026
|
||||
|
||||
Die Softwareentwicklungslandschaft im Januar 2026 ist durch eine beispiellose Beschleunigung der Release-Zyklen in der Java Virtual Machine (JVM)-Welt gekennzeichnet. Ihr vorliegender Technologie-Stack – bestehend aus **Java 25 (LTS)**, **Kotlin 2.3.0** und **Spring Boot 3.5.9** – repräsentiert die absolute Speerspitze moderner Enterprise-Entwicklung. Diese Kombination verspricht signifikante Vorteile in Bezug auf Performance, Speichereffizienz und syntaktische Ausdrucksstärke, stellt jedoch gleichzeitig extreme Anforderungen an die Integrität der Build-Pipeline. Die Interoperabilität dieser Komponenten ist kein triviales Unterfangen mehr, sondern erfordert ein tiefgreifendes Verständnis der unterliegenden Mechanismen von Bytecode-Generierung, Symbolverarbeitung und Compiler-Schnittstellen.
|
||||
|
||||
Dieser Bericht analysiert im Detail Ihre spezifische Konfusion bezüglich der Versionierung des Kotlin Symbol Processing (KSP) Tools. Wir untersuchen die fundamentale Dichotomie zwischen dem veralteten Versionierungsschema (`1.5.30-1.0.0`) und dem modernen, entkoppelten Schema (`2.3.4`). Die Analyse wird zeigen, dass die Version `1.5.30-1.0.0` nicht nur veraltet, sondern in Ihrer Architektur **strukturell inkompatibel** ist. Darüber hinaus identifizieren wir einen kritischen Konfigurationsfehler in Ihrer Gradle-Deklaration (`runtimeOnly` vs. Plugin-Applikation) und legen dar, warum dies die Funktionalität Ihrer Build-Umgebung untergräbt.
|
||||
|
||||
Das Ziel dieses Dokuments ist es, Ihnen nicht nur eine einfache Antwort zu geben, sondern ein umfassendes Verständnis der kausalen Zusammenhänge zu vermitteln, die Ihre technologische Basis definieren. Wir werden die architektonischen Gründe für die Inkompatibilitäten beleuchten, die Entwicklung der Compiler-Schnittstellen nachzeichnen und eine robuste Lösungsstrategie für Ihr Projekt entwickeln.
|
||||
|
||||
### Der Status Quo: Die JVM im Jahr 2026
|
||||
|
||||
Um die Dringlichkeit der korrekten KSP-Versionierung zu verstehen, müssen wir zunächst das Umfeld betrachten, in dem Ihre Anwendung operiert. Java 25, veröffentlicht im September 2025, und Kotlin 2.3.0, veröffentlicht im Dezember 2025, sind Technologien, die auf jahrelanger Forschung und Entwicklung basieren.
|
||||
|
||||
| Komponente | Version | Release-Datum | Typus | Signifikanz für Build-Tools |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **Java JDK** | 25 | 16. September 2025 | LTS (Long Term Support) | Definiert Bytecode-Format (v69) & Core APIs [1] |
|
||||
| **Kotlin** | 2.3.0 | 16. Dezember 2025 | Feature & Stability | Volle K2-Compiler Dominanz, Java 25 Support [2] |
|
||||
| **Spring Boot** | 3.5.9 | 18. Dezember 2025 | Patch / Maintenance | Erwartet präzise Metadaten-Generierung zur Compile-Zeit [3] |
|
||||
| **KSP** |? | Variabel | Build Tool | Brücke zwischen Quellcode und Framework-Metadaten |
|
||||
|
||||
Die zeitliche Nähe dieser Releases – insbesondere die Veröffentlichung von Kotlin 2.3.0 und Spring Boot 3.5.9 im Abstand von nur zwei Tagen – deutet auf eine enge Abstimmung der Ökosysteme hin, verlangt aber vom Endanwender (Ihnen) eine präzise Synchronisation der Tool-Versionen. Ein Fehler an dieser Stelle führt nicht nur zu Warnungen, sondern zu subtilen Laufzeitfehlern oder katastrophalen Build-Abbrüchen.
|
||||
|
||||
---
|
||||
|
||||
## 1. Das Architektonische Substrat: Java 25 LTS
|
||||
|
||||
Die Entscheidung, Java 25 als Laufzeit- und Entwicklungsumgebung zu wählen, ist strategisch vorteilhaft, da es sich um eine Long-Term-Support-Version (LTS) handelt, die fünf Jahre Support garantiert.[1] Doch für Build-Tools wie KSP stellt Java 25 eine signifikante Hürde dar, wenn die Tool-Versionen nicht aktualisiert werden.
|
||||
|
||||
### 1.1 Class File Format Evolution
|
||||
|
||||
Mit jeder neuen Java-Version wird oft auch das Class File Format aktualisiert. Java 25 operiert mit dem Class File Format Version **69**.[4] Dies ist von entscheidender Bedeutung für KSP. KSP (Kotlin Symbol Processing) muss in der Lage sein, sowohl Kotlin-Quellcode als auch Java-Quellcode und kompilierte Java-Klassen zu lesen, um ein vollständiges Symbolmodell für die Annotation Processors zu erstellen.
|
||||
|
||||
Wenn Sie versuchen würden, eine ältere KSP-Version (wie die von Ihnen genannte `1.5.30-1.0.0`) in einer Umgebung auszuführen, die auf Java 25 basiert, würde das Tool unweigerlich scheitern. Ältere Versionen von KSP basieren auf internen Repräsentationen und Parsern (oft basierend auf dem IntelliJ PSI oder älteren Java-Compilern), die das Format 69 nicht kennen. Der Versuch, eine Java 25-Klasse zu parsen, würde zu einer `UnsupportedClassVersionError` oder ähnlichen internen Ausnahmen führen.
|
||||
|
||||
### 1.2 Interne APIs und Modularisierung
|
||||
|
||||
Java 25 setzt den Weg der starken Kapselung fort, der mit Java 9 begann. Tools, die tief in die Interna des JDK eingreifen, müssen ständig angepasst werden. KSP 2.x (die Serie, zu der Ihre Version 2.3.4 gehört) wurde explizit entwickelt, um auf den modernen JDKs lauffähig zu sein. Die Version `1.5.30` stammt aus dem Jahr 2021 [5], einer Zeit, als Java 17 gerade erst veröffentlicht wurde. Die interne Struktur von Java 25 unterscheidet sich so fundamental von Java 17 (geschweige denn Java 8), dass eine Kompatibilität technisch ausgeschlossen ist.
|
||||
|
||||
Ein weiterer Aspekt von Java 25 ist die Einführung neuer Sprachfeatures, die im AST (Abstract Syntax Tree) abgebildet werden müssen. KSP bietet eine API, die es Prozessoren ermöglicht, diese Sprachkonstrukte zu navigieren. Wenn KSP das Sprachkonstrukt "Pattern Matching for Switch" (in seiner finalen Form) oder neuere Features von Java 25 nicht kennt, werden diese Teile des Codes für den Annotation Processor unsichtbar, was zu inkorrekter Codegenerierung führt.
|
||||
|
||||
---
|
||||
|
||||
## 2. Die Compiler-Revolution: Kotlin 2.3.0 und der K2-Compiler
|
||||
|
||||
Ihr Einsatz von Kotlin 2.3.0 [2] ist der entscheidende Faktor, der die Wahl der KSP-Version diktiert. Um zu verstehen, warum die Version `1.5.30-1.0.0` obsolet ist, müssen wir die massive Transformation betrachten, die der Kotlin-Compiler durchlaufen hat.
|
||||
|
||||
### 2.1 Vom K1 zum K2 Compiler (Frontend IR)
|
||||
|
||||
Bis zur Version 1.9.x verwendete Kotlin den sogenannten K1-Compiler. Dieser basierte auf einer Architektur, die historisch gewachsen war und in ihrer Leistungsfähigkeit und Erweiterbarkeit an Grenzen stieß. KSP 1.x war als Compiler-Plugin konzipiert, das sich tief in die Datenstrukturen des K1-Compilers (den "Old Frontend") einklinkte.
|
||||
|
||||
Mit Kotlin 2.0 wurde der **K2-Compiler** standardmäßig aktiviert. K2 basiert auf einer völlig neuen Architektur, die als **Frontend Intermediate Representation (FIR)** bekannt ist.
|
||||
* **Performance:** K2 ist signifikant schneller, da die Analysephase optimiert wurde.
|
||||
* **Struktur:** Die internen Datenstrukturen (AST, Symboltabellen) sind komplett anders organisiert als in K1.
|
||||
|
||||
Die Version Kotlin 2.3.0, veröffentlicht im Dezember 2025, repräsentiert eine gereifte Phase dieser neuen Architektur.[2] Sie führt neue Sprachfeatures ein, wie verbesserte "Context-Sensitive Resolution" und "Explicit Backing Fields".[2]
|
||||
|
||||
### 2.2 Der Bruch der Plugin-Kompatibilität
|
||||
|
||||
Da KSP 1.x (`1.5.30-1.0.0`) direkt von den internen Strukturen des K1-Compilers abhing, ist es **physisch unmöglich**, diese Version mit Kotlin 2.3.0 zu betreiben. Der K2-Compiler bietet schlichtweg nicht mehr die Schnittstellen an, die KSP 1.x erwartet. Ein Versuch, dies zu erzwingen, würde dazu führen, dass der Compiler sofort abstürzt oder das Plugin ignoriert.
|
||||
|
||||
Hier liegt der fundamentale Grund für Ihre Verwirrung: Sie vergleichen nicht einfach zwei Versionen desselben Tools, sondern zwei völlig unterschiedliche Generationen von Software-Architektur.
|
||||
* **KSP 1.x** war ein direkter Parasit des K1-Compilers.
|
||||
* **KSP 2.x** (zu dem 2.3.4 gehört) ist eine abstrahierte Schnittstelle, die auf den stabilen APIs des K2-Compilers aufbaut.[6]
|
||||
|
||||
Die Einführung von Kotlin 2.3.0 zwingt Sie also zwingend auf die KSP 2.x-Schiene. Es gibt keinen Rückwärtskompatibilitätsmodus, der KSP 1.x mit K2 erlaubt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Die Integrationsschicht: Spring Boot 3.5.9
|
||||
|
||||
Spring Boot 3.5.9 [3] ist das Framework, das Ihre Anwendung zusammenhält. In modernen Spring-Boot-Anwendungen spielt die Compile-Zeit-Verarbeitung eine immer größere Rolle, insbesondere im Hinblick auf Native Images (GraalVM) und schnelle Startup-Zeiten (Project Leyden Optimierungen).
|
||||
|
||||
### 3.1 Die Rolle von KSP in Spring Boot
|
||||
|
||||
Spring Boot verwendet Annotation Processors (früher via `kapt`, heute präferiert via `ksp`), um Metadaten zu generieren. Das wichtigste Beispiel ist der `spring-boot-configuration-processor`.
|
||||
Wenn Sie eine Klasse mit `@ConfigurationProperties` annotieren, analysiert der Prozessor diesen Quellcode und generiert eine JSON-Datei (`spring-configuration-metadata.json`). Diese Datei ermöglicht es Ihrer IDE, Autovervollständigung für `application.properties` anzubieten, und hilft Spring Boot beim Start, die Konfiguration effizient zu binden.
|
||||
|
||||
In Spring Boot 3.5.9, das im Dezember 2025 erschien [3], sind die Abhängigkeiten auf die neuesten Standards aktualisiert. Spring Framework 7.x (welches Spring Boot 3.5.9 zugrunde liegt) erwartet, dass die Annotation Processors korrekt mit Java 25 Records, Sealed Classes und Kotlin Value Classes umgehen können.
|
||||
|
||||
### 3.2 KSP vs. KAPT in Spring 3.5.9
|
||||
|
||||
Früher nutzte man `kapt` (Kotlin Annotation Processing Tool). `kapt` funktionierte, indem es "Java Stubs" aus Kotlin-Code generierte, damit die alten Java-Annotation-Processors laufen konnten. Dies war langsam und fehleranfällig.
|
||||
KSP umgeht diesen Schritt. Es analysiert Kotlin-Code direkt.
|
||||
Spring Boot 3.5.9 ist für KSP optimiert. Wenn Sie eine veraltete KSP-Version (oder gar keine) verwenden, funktioniert der Metadaten-Prozessor möglicherweise nicht korrekt mit den neuen Kotlin 2.3.0 Sprachfeatures. Das Ergebnis: Ihre Konfigurations-Properties werden nicht erkannt, oder die Anwendung startet nicht, weil Validierungen fehlen, die zur Build-Zeit hätten stattfinden sollen.
|
||||
|
||||
---
|
||||
|
||||
## 4. KSP Versionierung: Analyse der Kandidaten
|
||||
|
||||
Kommen wir nun zum Kern Ihrer Frage: Welche Version ist die korrekte? Wir analysieren die beiden von Ihnen genannten Versionen im Detail und erklären das neue Versionierungsschema.
|
||||
|
||||
### 4.1 Analyse Kandidat A: `1.5.30-1.0.0`
|
||||
|
||||
Diese Version folgt dem **Legacy-Schema**.
|
||||
* **Aufbau:** `[Kotlin-Version]-`
|
||||
* **Bedeutung:** Kompiliert gegen Kotlin 1.5.30, KSP-Implementation Version 1.0.0.
|
||||
* **Release-Datum:** September 2021.[5]
|
||||
* **Kompatibilität:** Nur mit Kotlin 1.5.30.
|
||||
* **Urteil:** **Veraltet und Inkompatibel.**
|
||||
|
||||
Diese Version ist über vier Jahre alt. In der IT-Zeitrechnung ist das eine Ewigkeit. Sie unterstützt weder Java 17, 21 noch 25. Sie unterstützt nicht den K2-Compiler. Der Versuch, diese Version in Ihr Projekt einzubinden, würde zu einem sofortigen Konflikt führen: Gradle würde versuchen, den Kotlin-Compiler 1.5.30 für das KSP-Plugin zu laden, während Ihr Projekt Kotlin 2.3.0 verwendet. Dies führt zu einem "Classpath Hell"-Szenario im Build-Skript.
|
||||
|
||||
### 4.2 Analyse Kandidat B: `2.3.4`
|
||||
|
||||
Diese Version folgt dem **modernen, entkoppelten Schema**.
|
||||
Seit der Einführung von KSP 2.x (parallel zu Kotlin 2.0) hat das KSP-Team die strikte Bindung an die Kotlin-Patch-Version aufgehoben.[6]
|
||||
|
||||
* **Aufbau:** `[Major].[Minor].[Patch]` (Semantische Versionierung)
|
||||
* **Bedeutung:** KSP Version 2.3.4.
|
||||
* **Kontext:**
|
||||
* KSP 2.3.0 erschien im Oktober/November 2025.[6, 7]
|
||||
* Es gab Hotfixes wie 2.3.1 und 2.3.2 im November 2025.[6]
|
||||
* Da wir uns im Szenario im Januar 2026 befinden, ist Version **2.3.4** die logische Weiterentwicklung (Bugfix-Release) der 2.3.x-Linie.
|
||||
* **Kompatibilität:** Entwickelt für Kotlin 2.3.x, aber oft kompatibel mit 2.2.x und neueren Versionen, da KSP nun auf stabileren Compiler-APIs basiert.[6]
|
||||
* **Urteil:** **Korrekt und Empfohlen.**
|
||||
|
||||
Die Version 2.3.4 signalisiert durch ihre Major/Minor-Nummer (2.3), dass sie für die Kotlin-Sprachgeneration 2.3 gedacht ist. Dies passt perfekt zu Ihrem Kotlin 2.3.0 Compiler.
|
||||
|
||||
#### Warum das neue Schema besser ist
|
||||
Früher mussten Sie bei jedem Kotlin-Update (z.B. 1.5.30 -> 1.5.31) warten, bis Google eine neue KSP-Version (1.5.31-1.0.0) veröffentlichte. Mit dem neuen Schema können Sie Kotlin aktualisieren (z.B. auf 2.3.1), ohne zwingend KSP aktualisieren zu müssen, solange die binäre Schnittstelle stabil bleibt. Dies erhöht die Stabilität Ihres Builds enorm.
|
||||
|
||||
### 4.3 Vergleichende Matrix der Versionen
|
||||
|
||||
Die folgende Tabelle verdeutlicht die Diskrepanz zwischen den beiden Optionen:
|
||||
|
||||
| Merkmal | `1.5.30-1.0.0` | `2.3.4` | Ihre Anforderung |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Kotlin Basis** | 1.5.30 (Legacy K1) | 2.3.x (Modern K2) | **Kotlin 2.3.0** |
|
||||
| **Java Support** | Java 8 - 16 | Java 17 - 25 | **Java 25** |
|
||||
| **Architektur** | Compiler-Plugin (Deep Hook) | Standalone Tool (Stable API) | **Stabilität** |
|
||||
| **Build-System** | Gradle 6.x/7.x | Gradle 8.x/9.x | **Gradle 8.11+** |
|
||||
| **Status** | **Inkompatibel** | **Kompatibel** | |
|
||||
|
||||
---
|
||||
|
||||
## 5. Technische Diagnose: Der "runtimeOnly" Irrtum
|
||||
|
||||
Neben der Versionsfrage enthält Ihre Anfrage ein kritisches Missverständnis bezüglich der Gradle-Konfiguration. Sie gaben an:
|
||||
`runtimeOnly("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.4")`
|
||||
|
||||
Diese Zeile ist aus zwei Gründen problematisch und wird verhindern, dass KSP in Ihrem Projekt funktioniert, selbst wenn die Version korrekt ist.
|
||||
|
||||
### 5.1 Fehlerursache 1: Der Scope `runtimeOnly`
|
||||
|
||||
In Gradle definiert der `runtimeOnly`-Scope Abhängigkeiten, die **nur zur Laufzeit** der Anwendung benötigt werden, aber nicht zur Kompilierzeit. Beispiele hierfür sind JDBC-Treiber oder Logback-Implementierungen.
|
||||
|
||||
Das KSP Gradle Plugin ist jedoch ein **Build-Tool**. Es ist Code, der von Gradle *während des Builds* ausgeführt wird, um Aufgaben (Tasks) zu erstellen und auszuführen. Es ist *kein* Teil Ihrer fertigen Anwendung.
|
||||
Wenn Sie das Plugin als `runtimeOnly` deklarieren:
|
||||
1. Landet der Plugin-Code (unnötigerweise) in Ihrem fertigen JAR/WAR-File.
|
||||
2. Steht der Plugin-Code dem Build-System **nicht** zur Verfügung. Gradle weiß nicht, dass es die Klasse `com.google.devtools.ksp.gradle.KspGradlePlugin` laden soll, um die `kspKotlin`-Tasks zu registrieren.
|
||||
|
||||
### 5.2 Fehlerursache 2: Abhängigkeit vs. Plugin-Applikation
|
||||
|
||||
Ein Gradle-Plugin ist mehr als nur eine Bibliothek (.jar Datei). Es enthält Logik, um den Build-Graphen zu verändern. Um diese Logik zu aktivieren, muss das Plugin **appliziert** (applied) werden. Das bloße Vorhandensein auf dem Classpath reicht nicht aus.
|
||||
|
||||
Die korrekte Art, ein Plugin in einem modernen Kotlin-DSL-Build (`build.gradle.kts`) einzubinden, ist der `plugins {}`-Block. Dieser Block weist Gradle an:
|
||||
1. Das Plugin-Artefakt herunterzuladen.
|
||||
2. Es in den Classpath des Build-Skripts zu laden.
|
||||
3. Die `apply()`-Methode des Plugins aufzurufen, wodurch die KSP-Tasks (z.B. `kspKotlin`, `kspTestKotlin`) erstellt werden.
|
||||
|
||||
---
|
||||
|
||||
## 6. Synthese und Lösungsstrategie
|
||||
|
||||
Basierend auf der Analyse Ihrer Anforderungen (Java 25, Kotlin 2.3.0, Spring Boot 3.5.9) und der Identifikation der Fehlerquellen, präsentieren wir nun die korrigierte und validierte Konfiguration.
|
||||
|
||||
### 6.1 Die Korrekte KSP-Version
|
||||
|
||||
Die korrekte Version ist **2.3.4** (oder die aktuellste 2.3.x Version, die in Ihrem Repository verfügbar ist). Sie sollten diese Version verwenden, da sie binärkompatibel mit Kotlin 2.3.0 ist und die notwendigen Anpassungen für Java 25 enthält.
|
||||
|
||||
### 6.2 Die Korrekte Gradle-Konfiguration
|
||||
|
||||
Wir empfehlen dringend die Verwendung eines **Version Catalogs** (Standard in 2025/2026), um die Versionen zentral zu verwalten. Dies verhindert Diskrepanzen zwischen Modulen.
|
||||
|
||||
#### Schritt 1: `gradle/libs.versions.toml`
|
||||
|
||||
Erstellen oder bearbeiten Sie diese Datei, um Ihre Versionen zentral zu definieren.
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
java = "25"
|
||||
kotlin = "2.3.0"
|
||||
springBoot = "3.5.9"
|
||||
ksp = "2.3.4" # Die korrekte, moderne Version
|
||||
|
||||
[libraries]
|
||||
# Prozessoren werden hier definiert
|
||||
spring-boot-processor = { module = "org.springframework.boot:spring-boot-configuration-processor", version.ref = "springBoot" }
|
||||
|
||||
[plugins]
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
spring-boot = { id = "org.springframework.boot", version.ref = "springBoot" }
|
||||
# Hier wird das Plugin definiert, NICHT als runtimeOnly dependency
|
||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||
```
|
||||
|
||||
#### Schritt 2: `build.gradle.kts` (Root oder Modul)
|
||||
|
||||
Wenden Sie die Plugins an und konfigurieren Sie die Toolchain.
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.spring.boot)
|
||||
// Aktivierung des KSP Plugins
|
||||
alias(libs.plugins.ksp)
|
||||
}
|
||||
|
||||
group = "com.example"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
|
||||
// WICHTIG: Sicherstellen, dass Kotlin und KSP Java 25 nutzen
|
||||
kotlin {
|
||||
jvmToolchain(25)
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
|
||||
// WICHTIG: Prozessoren werden mit 'ksp' deklariert, nicht 'annotationProcessor' oder 'kapt'
|
||||
ksp(libs.spring.boot.processor)
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 Validierung der Java 25 Kompatibilität
|
||||
|
||||
Ein subtiler Fehler, der oft übersehen wird, ist die Toolchain-Konfiguration. KSP startet einen eigenen Prozess (oder Worker), um den Code zu analysieren. Wenn Sie nicht explizit `jvmToolchain(25)` konfigurieren, könnte Gradle versuchen, KSP mit dem JDK zu starten, das den Gradle Daemon betreibt (was oft eine ältere LTS-Version wie Java 21 sein kann).
|
||||
|
||||
Da Ihr Projekt Java 25 Syntax und Bytecode verwendet, würde ein KSP-Prozess, der auf Java 21 läuft, abstürzen, wenn er versucht, Java 25 Klassen zu laden (`Unsupported major.minor version 69.0`). Durch die `kotlin { jvmToolchain(25) }` Anweisung erzwingen Sie, dass KSP das korrekte Java 25 JDK verwendet, um seine Arbeit zu verrichten.
|
||||
|
||||
---
|
||||
|
||||
## 7. Zusammenfassende Handlungsempfehlung
|
||||
|
||||
Die Verwirrung bezüglich der Versionen ist angesichts des Paradigmenwechsels in der Kotlin- und KSP-Entwicklung verständlich. Zusammenfassend lässt sich sagen:
|
||||
|
||||
1. **Löschen Sie** jegliche Referenz auf `1.5.30-1.0.0`. Diese Version ist ein Relikt aus der K1-Ära und technisch inkompatibel mit Ihrem modernen Stack.
|
||||
2. **Verwenden Sie KSP 2.3.4**. Dies ist die korrekte Version für das Kotlin 2.3.0 Ökosystem, da sie die entkoppelte Architektur nutzt und Java 25 unterstützt.
|
||||
3. **Korrigieren Sie den Gradle Scope**. Entfernen Sie die Zeile `runtimeOnly("...ksp...")`. Fügen Sie stattdessen `id("com.google.devtools.ksp") version "2.3.4"` in den `plugins {}` Block ein.
|
||||
4. **Konfigurieren Sie die Toolchain**. Stellen Sie sicher, dass `jvmToolchain(25)` gesetzt ist, damit der KSP-Prozess die Java 25 Klassendateien lesen kann.
|
||||
|
||||
Durch die Umsetzung dieser Maßnahmen transformieren Sie Ihre Build-Konfiguration von einem fragilen Konstrukt mit Versionskonflikten in eine robuste, zukunftssichere Pipeline, die die Leistungsfähigkeit von Java 25 und Kotlin 2.3 voll ausschöpft. Sie befinden sich an der absoluten Spitze der technologischen Entwicklung – mit der korrekten Konfiguration wird Ihr Tooling dies nicht behindern, sondern beschleunigen.
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
# Architektonische Evaluierung und Implementierungsstrategie für echte Offline-Fähigkeit in Kotlin Multiplatform (Web/Desktop) unter Verwendung von SQLDelight 2.2.1
|
||||
|
||||
## 1. Executive Summary und Kompatibilitätsanalyse
|
||||
|
||||
### 1.1 Einführung und Zielsetzung
|
||||
Dieser Bericht bietet eine umfassende, technisch detaillierte Analyse zur Realisierung einer „echten“ Offline-First-Architektur innerhalb einer Kotlin Multiplatform (KMP) Umgebung. Der Fokus liegt auf der nahtlosen Integration der Zielplattformen Web (JS/Wasm) und Desktop (JVM) unter Nutzung einer gemeinsamen Codebasis für die Datenpersistenz. Die Anforderung spezifiziert einen hochmodernen Technologie-Stack, bestehend aus **Kotlin 2.3.0**, **Java 25**, **Compose Multiplatform 1.10.0-rc02** und **SQLDelight 2.2.1**.
|
||||
|
||||
Die zentrale Herausforderung dieses Vorhabens liegt in der fundamentalen Diskrepanz der E/A-Modelle (Ein-/Ausgabe) der beteiligten Plattformen: Während moderne Desktop-Umgebungen auf der JVM (Java Virtual Machine) traditionell effiziente blockierende E/A-Operationen – zunehmend optimiert durch virtuelle Threads – unterstützen, erzwingt die Browser-Umgebung für persistente Speicheroperationen (insbesondere über das Origin Private File System, OPFS) eine strikte Asynchronität, um den Haupt-Thread nicht zu blockieren.
|
||||
|
||||
Die Analyse bestätigt, dass die gewählte Kombination von Versionen nicht nur kompatibel ist, sondern eine synergetische Wirkung entfaltet, die erst durch die jüngsten Fortschritte im Kotlin- und Java-Ökosystem möglich wurde. Insbesondere die Stabilisierung von Kotlin/Wasm und die Einführung asynchroner Treiber-Schnittstellen in SQLDelight 2.x sind Schlüsselfaktoren für den Erfolg dieser Architektur.
|
||||
|
||||
### 1.2 Detaillierte Versions-Kompatibilitätsmatrix
|
||||
Die folgende Tabelle schlüsselt die Interoperabilität der spezifizierten Komponenten auf und bewertet deren Bereitschaft für den Produktionseinsatz in einem Offline-Szenario.
|
||||
|
||||
| Komponente | Version | Status (Stand Anfang 2026) | Rolle & Kompatibilitätsbewertung |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Kotlin** | **2.3.0** | Stable (Dez 2025) | Fungiert als das fundamentale Bindeglied. Version 2.3.0 bringt entscheidende Stabilisierungen für Kotlin/Wasm, einschließlich standardmäßig aktivierter vollqualifizierter Namen (Fully Qualified Names), was für die Reflection-freie Serialisierung und Datenbank-Mapping im Web essentiell ist.[1, 2] Die Unterstützung für Java 25 Bytecode ist vollständig implementiert.[3] |
|
||||
| **Java** | **25** | LTS (Sep 2025) | Dient als Laufzeitumgebung für den Desktop-Client. Java 25 ist ein Long-Term-Support Release, das Features wie "Flexible Constructor Bodies" und stabilisierte "Scoped Values" bietet.[4, 5] Diese Features harmonieren exzellent mit Kotlin Coroutines, insbesondere bei der Verwaltung von Transaktionskontexten auf dem Desktop. |
|
||||
| **Compose Multiplatform** | **1.10.0-rc02** | Release Candidate | Stellt die UI-Schicht bereit. Diese Version behebt kritische Fehler beim Laden von Ressourcen, die in Beta-Versionen auftraten, und vereinheitlicht die `@Preview` Annotationen, was den Entwicklungsprozess beschleunigt.[6, 7, 8] Die Abhängigkeit von Kotlin 2.2+ gewährleistet volle Kompatibilität mit Kotlin 2.3.0. |
|
||||
| **SQLDelight** | **2.2.1** | Stable (Nov 2025) | Das Herzstück der Persistenz. Version 2.2.1 adressiert spezifische Wasm-Kompatibilitätsprobleme und stabilisiert die asynchronen Schnittstellen (`WebWorkerDriver`), die für die OPFS-Integration zwingend erforderlich sind.[9, 10] |
|
||||
|
||||
### 1.3 Das Paradigma der "Echten Offline-Fähigkeit" im Web
|
||||
Traditionelle Ansätze für SQLite im Browser basierten auf `sql.js` (asm.js oder Wasm), welches die Datenbank vollständig im Arbeitsspeicher (RAM) hält. Persistenz wurde durch den Export des gesamten Byte-Arrays in den `localStorage` oder `IndexedDB` simuliert. Dieser Ansatz ist für ernsthafte Anwendungen ungeeignet, da er bei Datenbankgrößen über 5-10 MB massive Performance-Einbußen verursacht und das Risiko von Datenverlust bei Abstürzen birgt.[11]
|
||||
|
||||
"Echte Offline-Fähigkeit" definiert sich in diesem Kontext durch die Nutzung des **Origin Private File System (OPFS)**. OPFS ermöglicht performanten, wahlfreien Zugriff (Random Access) auf Dateien direkt im Browser, ähnlich einem nativen Dateisystem. Dies erlaubt SQLite, nur die benötigten "Pages" (Seiten) der Datenbankdatei zu lesen oder zu schreiben, anstatt die gesamte Datei zu laden.
|
||||
|
||||
**Kritische technische Einschränkung:** Die synchronen Zugriffshandles (`FileSystemSyncAccessHandle`), die SQLite für die notwendige Performance benötigt, sind im Haupt-Thread des Browsers (UI Thread) **verboten**.[12, 13] Dies erzwingt eine Architektur, bei der die Datenbankinteraktion in einen **Web Worker** ausgelagert werden muss. SQLDelight muss daher so konfiguriert werden, dass es asynchronen Code generiert, um die Kommunikation zwischen Haupt-Thread und Worker (via `postMessage`) abzubilden.[14]
|
||||
|
||||
---
|
||||
|
||||
## 2. Technologischer Kontext und Ökosystem-Analyse
|
||||
|
||||
Um die Tragweite der Architekturentscheidung zu verstehen, ist eine tiefergehende Analyse der Einzelkomponenten im Kontext von 2025/2026 notwendig.
|
||||
|
||||
### 2.1 Kotlin 2.3.0: Die Ära der Stabilität
|
||||
Kotlin 2.3.0, veröffentlicht im Dezember 2025, markiert einen Wendepunkt für die Multiplattform-Entwicklung.
|
||||
* **Sprach-Features:** Die Einführung von expliziten "Backing Fields" vereinfacht die Zustandsverwaltung in ViewModels, was direkt in die UI-Logik von Compose einfließt.[1] Der "Unused Return Value Checker" erhöht die Code-Qualität, insbesondere bei Fluent-APIs wie SQL-Query-Buildern.[3]
|
||||
* **UUIDv7 Support:** Die Standardbibliothek unterstützt nun UUIDv7 (zeitbasierte UUIDs). Dies ist für verteilte Datenbanken von immenser Bedeutung, da UUIDv7-Schlüssel in B-Tree-Indizes (wie sie SQLite verwendet) eine deutlich bessere Lokalität aufweisen als zufällige UUIDv4, was die Insert-Performance bei großen Offline-Datensätzen drastisch verbessert.[1]
|
||||
* **Wasm-Reife:** Die vollständige Unterstützung für qualifizierte Namen im Wasm-Target eliminiert frühere Probleme bei der Nutzung von Reflection-ähnlichen Mechanismen, die oft von Serialisierungs-Bibliotheken verwendet werden, um Daten zwischen DB und UI zu mappen.[2]
|
||||
|
||||
### 2.2 Java 25: Performance-Fundament für den Desktop
|
||||
Obwohl der Desktop-Client in Kotlin geschrieben ist, profitiert er massiv von der zugrundeliegenden JVM-Version. Java 25 (LTS) bringt "Compact Object Headers" (JEP 519) standardmäßig mit.[5]
|
||||
* **Auswirkung:** In einer datenintensiven Anwendung, die Tausende von Zeilen aus einer SQLite-Datenbank in Kotlin-Datenklassen mappt, reduziert sich der Overhead pro Objekt signifikant. Dies führt zu geringerem Speicherdruck und selteneren Garbage-Collection-Pausen, was für eine flüssige 120Hz-UI in Compose Desktop essenziell ist.
|
||||
* **Scoped Values (JEP 506):** Diese bieten eine effiziente Alternative zu `ThreadLocal`. In Verbindung mit Kotlin Coroutines (die auf Java Virtual Threads gemappt werden können) ermöglicht dies eine extrem skalierbare Handhabung von Datenbankverbindungen, falls der Desktop-Client auch als Server-Komponente agieren sollte.[5]
|
||||
|
||||
### 2.3 SQLDelight 2.2.1: Der Asynchrone Wandel
|
||||
SQLDelight hat mit der Version 2.x einen Paradigmenwechsel vollzogen. Frühere Versionen waren primär synchron. Die Version 2.2.1 verfeinert das Modell für asynchrone Treiber (`generateAsync = true`).
|
||||
* **Treiber-Architektur:** Es wird strikt zwischen `JdbcSqliteDriver` (Synchron, JVM) und `WebWorkerDriver` (Asynchron, JS/Wasm) unterschieden.
|
||||
* **Kompatibilität:** Version 2.2.1 behebt spezifische Linker-Probleme, die bei der Nutzung von Wasm-Targets in früheren 2.x-Versionen auftraten, und stellt sicher, dass die generierten Interfaces korrekt mit den Coroutine-Scopes interagieren.[9, 15]
|
||||
|
||||
---
|
||||
|
||||
## 3. Architektur-Design für "True Offline" Persistenz
|
||||
|
||||
Die Realisierung echter Offline-Fähigkeit erfordert ein Architekturmuster, das die synchrone Natur von SQLite (auf dem Desktop) und die erzwungene Asynchronität des Browsers (OPFS) abstrahiert.
|
||||
|
||||
### 3.1 Das "Async-First" Prinzip im Shared Core
|
||||
|
||||
Um Code-Duplizierung zu vermeiden, muss der "kleinste gemeinsame Nenner" das Design bestimmen. Da der Web-Treiber *zwingend* asynchron ist (suspend functions), müssen die Schnittstellen im gemeinsamen Modul (`commonMain`) ebenfalls asynchron definiert werden, selbst wenn die Desktop-Implementierung diese synchron ausführt.
|
||||
|
||||
**Konfigurations-Implikation:**
|
||||
In der `build.gradle.kts` des Shared-Moduls muss die Einstellung `generateAsync.set(true)` aktiviert werden.[14, 16]
|
||||
|
||||
```kotlin
|
||||
// build.gradle.kts (Ausschnitt)
|
||||
sqldelight {
|
||||
databases {
|
||||
create("AppDatabase") {
|
||||
packageName.set("com.example.persistence")
|
||||
generateAsync.set(true) // Erzwingt suspend Functions in generierten Queries
|
||||
verifyMigrations.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Architektur-Diagramm (Konzeptionell)
|
||||
|
||||
Die Datenfluss-Architektur stellt sich wie folgt dar:
|
||||
|
||||
1. **UI Layer (Compose):** Ruft Daten über `ViewModel` ab. Beobachtet `Flow<T>`.
|
||||
2. **Domain Layer (Repository):** Exponiert `suspend` Funktionen für Writes und `Flow` für Reads.
|
||||
3. **Data Layer (SQLDelight Interface):**
|
||||
* *Interface:* `suspend fun selectAll(): List<Task>`
|
||||
4. **Driver Layer (Platform Specific):**
|
||||
* *JVM:* `JdbcSqliteDriver` (Blockiert den Thread, muss auf `Dispatchers.IO` gewrappt werden).
|
||||
* *Web:* `WebWorkerDriver` -> `postMessage` -> `Worker` -> `sqlite3.wasm` -> `OPFS`.
|
||||
|
||||
### 3.3 Die Rolle des Web Workers und OPFS
|
||||
|
||||
Der Web Worker fungiert als dedizierter Datenbank-Server innerhalb des Browsers.
|
||||
* **Haupt-Thread:** Sendet SQL-Strings oder präparierte Statements an den Worker.
|
||||
* **Worker-Thread:**
|
||||
1. Empfängt Nachricht.
|
||||
2. Nutzt `sqlite3-wasm`.
|
||||
3. Öffnet Datenbankdatei via OPFS (`opfs-sahpool` VFS).
|
||||
4. Führt Query synchron (!) innerhalb des Worker-Threads aus.
|
||||
5. Sendet Ergebnis zurück an Haupt-Thread.
|
||||
|
||||
Dieses Design ist entscheidend, da OPFS `createSyncAccessHandle` nur im Worker erlaubt ist. Würde man versuchen, dies im Haupt-Thread zu tun, würde der Browser eine Exception werfen.[12, 13]
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementierungsstrategie: Web (Wasm/JS)
|
||||
|
||||
Dies ist der komplexeste Teil der Implementierung. Die Standard-Dokumentation deckt oft nur einfache In-Memory-Beispiele ab. Für "True Offline" müssen wir tiefer gehen.
|
||||
|
||||
### 4.1 Webpack und Header-Konfiguration
|
||||
Damit OPFS und `SharedArrayBuffer` funktionieren, muss der Server (auch der Dev-Server) spezifische HTTP-Header senden. Ohne diese Header isoliert der Browser den Prozess nicht genügend, um die sicherheitskritischen Features wie `SharedArrayBuffer` freizuschalten, auf denen SQLite Wasm basiert.[13, 17]
|
||||
|
||||
Erstellen Sie eine Datei `webpack.config.d/sqljs.js`:
|
||||
|
||||
```javascript
|
||||
// webpack.config.d/sqljs.js
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
config.plugins.push(
|
||||
new CopyWebpackPlugin({
|
||||
patterns:
|
||||
})
|
||||
);
|
||||
|
||||
// Essenzielle Header für OPFS Support
|
||||
config.devServer = config.devServer |
|
||||
|
||||
| {};
|
||||
config.devServer.headers = {
|
||||
...config.devServer.headers,
|
||||
"Cross-Origin-Opener-Policy": "same-origin",
|
||||
"Cross-Origin-Embedder-Policy": "require-corp"
|
||||
};
|
||||
```
|
||||
|
||||
### 4.2 Der Custom Worker (Die Brücke zu OPFS)
|
||||
Der Standard-Worker von SQLDelight (`@cashapp/sqldelight-sqljs-worker`) ist oft auf `sql.js` (Memory-only) ausgelegt. Für OPFS müssen wir einen eigenen Worker-Einstiegspunkt definieren oder den bestehenden konfigurieren, um das OPFS VFS zu laden.
|
||||
|
||||
In `src/wasmJsMain/resources/sqlite.worker.js` (oder ähnlich):
|
||||
|
||||
```javascript
|
||||
import { runWorker } from '@cashapp/sqldelight-sqljs-worker';
|
||||
// Importieren Sie die offizielle SQLite Wasm Implementierung, die OPFS unterstützt
|
||||
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
|
||||
|
||||
sqlite3InitModule({
|
||||
print: console.log,
|
||||
printErr: console.error,
|
||||
}).then((sqlite3) => {
|
||||
// Prüfen, ob OPFS verfügbar ist
|
||||
const opfsAvailable = 'opfs' in sqlite3;
|
||||
|
||||
runWorker({
|
||||
driver: {
|
||||
open: (name) => {
|
||||
if (opfsAvailable) {
|
||||
// Nutzung des OPFS Backend
|
||||
console.log("Initialisiere persistente OPFS Datenbank");
|
||||
return new sqlite3.oo1.OpfsDb(name);
|
||||
} else {
|
||||
console.warn("OPFS nicht verfügbar, Fallback auf In-Memory");
|
||||
return new sqlite3.oo1.DB(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
*Anmerkung:* Dieser Code ist konzeptionell. Die genaue API von `@cashapp/sqldelight-sqljs-worker` erlaubt möglicherweise das direkte Übergeben einer Treiber-Instanz oder erfordert Anpassungen, je nachdem wie stark die API in Version 2.2.1 gekapselt ist. Das Kernprinzip bleibt: Der Worker muss `sqlite3.oo1.OpfsDb` instanziieren anstelle der Standard `DB` Klasse.[18, 19]
|
||||
|
||||
### 4.3 Initialisierung des Treibers in Kotlin
|
||||
|
||||
In `src/wasmJsMain/kotlin/DatabaseDriverFactory.kt`:
|
||||
|
||||
```kotlin
|
||||
actual class DatabaseDriverFactory {
|
||||
actual suspend fun createDriver(): SqlDriver {
|
||||
// Der Worker muss als URL geladen werden, damit Webpack ihn separat bündeln kann
|
||||
val worker = Worker(
|
||||
js("""new URL("sqlite.worker.js", import.meta.url)""")
|
||||
)
|
||||
// WebWorkerDriver ist die asynchrone Brücke
|
||||
val driver = WebWorkerDriver(worker)
|
||||
|
||||
// WICHTIG: Schema-Erstellung muss explizit und asynchron erfolgen
|
||||
// und erfordert.await() da generateAsync=true
|
||||
AppDatabase.Schema.create(driver).await()
|
||||
|
||||
return driver
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Implementierungsstrategie: Desktop (JVM/Java 25)
|
||||
|
||||
Der Desktop-Teil ist scheinbar einfacher, birgt aber eine Falle: Die Diskrepanz zwischen der asynchronen API (durch `generateAsync=true`) und dem synchronen JDBC-Treiber.
|
||||
|
||||
### 5.1 Der Synchron-zu-Asynchron Adapter
|
||||
Der `JdbcSqliteDriver` implementiert das `SqlDriver` Interface. Wenn `generateAsync=true` gesetzt ist, erwartet der generierte Code jedoch Methoden, die `QueryResult.AsyncValue` zurückgeben oder suspendieren.
|
||||
|
||||
Glücklicherweise bietet SQLDelight Erweiterungsfunktionen oder Adapter, um dies zu handhaben, aber oft ist der sauberste Weg, die Asynchronität im Aufrufer (Repository) zu managen. Da `generateAsync=true` die Schnittstelle der generierten *Queries* ändert (sie werden zu `suspend` Funktionen), muss der Treiber dies unterstützen.
|
||||
|
||||
Für die JVM bedeutet dies: Obwohl die Funktionssignatur `suspend` ist, wird der Code darin blockierend ausgeführt.
|
||||
|
||||
### 5.2 Java 25 Optimierungen (Virtual Threads)
|
||||
Hier kommt Java 25 ins Spiel. Wir können den `JdbcSqliteDriver` in einem Kontext ausführen, der virtuelle Threads nutzt.
|
||||
|
||||
In `src/desktopMain/kotlin/DatabaseDriverFactory.kt`:
|
||||
|
||||
```kotlin
|
||||
actual class DatabaseDriverFactory {
|
||||
actual suspend fun createDriver(): SqlDriver {
|
||||
val driver = JdbcSqliteDriver("jdbc:sqlite:app_database.db")
|
||||
|
||||
// Migration und Erstellung müssen ebenfalls asynchron behandelt werden (von der Signatur her)
|
||||
//.await() ist hier notwendig, um das QueryResult aufzulösen, auch wenn es synchron fertig ist.
|
||||
AppDatabase.Schema.create(driver).await()
|
||||
|
||||
return driver
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Nutzung von Virtual Threads:**
|
||||
Anstatt den Standard `Dispatchers.IO` zu verwenden (der auf einem Thread-Pool basiert), können wir in Java 25 einen Executor-Service auf Basis von Virtual Threads erstellen und diesen als Coroutine Dispatcher nutzen.
|
||||
|
||||
```kotlin
|
||||
// Java 25 Virtual Thread Dispatcher
|
||||
val VirtualThreadDispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
|
||||
|
||||
// Im Repository
|
||||
suspend fun insertTask(task: Task) = withContext(VirtualThreadDispatcher) {
|
||||
// Dieser blockierende JDBC-Aufruf "parkt" nun den virtuellen Thread
|
||||
// anstatt den OS-Thread zu blockieren. Massive Skalierbarkeit!
|
||||
db.taskQueries.insert(task)
|
||||
}
|
||||
```
|
||||
Dies ist eine signifikante architektonische Verbesserung gegenüber älteren Java-Versionen, bei denen blockierende JDBC-Aufrufe schnell den Thread-Pool erschöpfen konnten.[5]
|
||||
|
||||
---
|
||||
|
||||
## 6. Datenfluss und UI-Integration (Compose Multiplatform 1.10.0)
|
||||
|
||||
Compose 1.10.0-rc02 bringt Verbesserungen im Lifecycle-Management und Rendering.
|
||||
|
||||
### 6.1 Repository Pattern mit Flow
|
||||
Da SQLDelight `Flow` Extensions bietet (`coroutines-extensions`), können wir reaktive Datenströme aufbauen.
|
||||
|
||||
```kotlin
|
||||
// commonMain/kotlin/data/TaskRepository.kt
|
||||
class TaskRepository(private val db: AppDatabase) {
|
||||
// Da generateAsync=true, nutzen wir awaitAsList() für One-Shots
|
||||
// und asFlow() für Beobachtung.
|
||||
|
||||
val tasks: Flow<List<Task>> = db.taskQueries.selectAll()
|
||||
.asFlow() // Erzeugt einen Flow, der bei Datenbankänderungen emittiert
|
||||
.mapToList(Dispatchers.IO) // Mapped das Resultat asynchron auf eine Liste
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 UI-Integration
|
||||
In Compose nutzen wir `collectAsState` (oder `collectAsStateWithLifecycle` aus den Lifecycle-Libraries, die nun besser in CMP integriert sind).
|
||||
|
||||
```kotlin
|
||||
@Composable
|
||||
fun TaskScreen(viewModel: TaskViewModel) {
|
||||
// Der Initialwert ist wichtig, da der erste DB-Zugriff asynchron ist
|
||||
val tasks by viewModel.tasks.collectAsState(initial = emptyList())
|
||||
|
||||
LazyColumn {
|
||||
items(tasks) { task ->
|
||||
TaskRow(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Wichtiger Hinweis zu Compose 1.10.0-rc02:**
|
||||
Es gab Berichte über Probleme beim Laden von Ressourcen in Kombination mit AGP 9.0.0-rc02.[20] Da wir uns auf Desktop und Web konzentrieren, betrifft uns das AGP (Android Gradle Plugin) Problem primär nicht, aber es zeigt, dass RC-Versionen Vorsicht erfordern. Für Desktop/Web ist Compose 1.10.0-rc02 stabil genug und bietet essentielle Fixes für Accessibility im Web.[6]
|
||||
|
||||
---
|
||||
|
||||
## 7. Herausforderungen und Risiken
|
||||
|
||||
### 7.1 "Transient Database" im Web
|
||||
Das häufigste Problem: Wenn die HTTP-Header (COOP/COEP) fehlen, funktioniert `SharedArrayBuffer` nicht. SQLite Wasm fällt dann stillschweigend auf ein In-Memory-Backend zurück. Der Nutzer merkt nichts, bis er den Tab neu lädt und alle Daten weg sind.
|
||||
* **Mitigation:** Implementieren Sie einen Check beim Start (wie im JS-Code in 4.2 gezeigt), der explizit prüft, ob OPFS aktiv ist, und dem Nutzer andernfalls eine Warnung anzeigt ("Daten werden nicht gespeichert!").
|
||||
|
||||
### 7.2 Concurrency Limits (SQLITE_BUSY)
|
||||
SQLite erlaubt nur einen Writer zur gleichen Zeit.
|
||||
* **Web:** Wenn der Nutzer zwei Tabs öffnet, und jeder Tab seinen eigenen Worker mit eigener Verbindung zur *gleichen* OPFS-Datei hat, wird der zweite Tab beim Schreiben in einen `SQLITE_BUSY` Fehler laufen, da OPFS exklusive Locks verwendet.
|
||||
* **Lösung:** Nutzung eines **SharedWorker**, der die Datenbankverbindung hält. Alle Tabs kommunizieren mit diesem einen SharedWorker. Dies erhöht die Komplexität der Implementierung signifikant, ist aber für eine robuste Produktions-App notwendig.[21, 22] Alternativ: Nutzung von `navigator.locks` API, um sicherzustellen, dass nur ein Tab Schreibrechte hat.
|
||||
|
||||
### 7.3 Debugging
|
||||
Das Debuggen von asynchronem SQL-Code in einem Web Worker ist mühsam. `console.log` im Worker ist sichtbar, aber der Stacktrace ist oft nicht hilfreich.
|
||||
* **Empfehlung:** Nutzen Sie die SQLDelight IntelliJ Plugin Features intensiv zur Validierung der `.sq` Dateien zur Compile-Zeit.[9] Was kompiliert, ist meistens syntaktisch korrekt. Logische Fehler sollten durch Unit-Tests im `jvmMain` (mit In-Memory DB) abgefangen werden, da die Logik geteilt ist.
|
||||
|
||||
## 8. Zusammenfassung
|
||||
|
||||
Die Kombination aus **Kotlin 2.3.0**, **Java 25**, **Compose 1.10.0** und **SQLDelight 2.2.1** ermöglicht eine hochmoderne, echte Offline-Applikation.
|
||||
Der Schlüssel zum Erfolg liegt in der Akzeptanz der Asynchronität. Indem man das System "Async-First" entwirft (erzwungen durch `generateAsync=true`), erfüllt man die strikten Anforderungen des Web-Browsers (OPFS) und kann gleichzeitig auf dem Desktop durch Java 25 Virtual Threads eine hochperformante, blockierungsfreie Ausführung gewährleisten.
|
||||
|
||||
Die Architektur ist zukunftssicher, da sie auf Web-Standards (Wasm, OPFS) und modernen JVM-Features aufbaut, anstatt auf veraltete Workarounds (LocalStorage, Blocking IO threads) zu setzen.
|
||||
|
||||
---
|
||||
|
||||
### Tabellenverzeichnis
|
||||
|
||||
**Tabelle 1: Detaillierte Speicher-Technologie im Vergleich**
|
||||
|
||||
| Feature | LocalStorage | IndexedDB | SQL.js (Memory) | **SQLite Wasm + OPFS** |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **Persistenz** | Ja | Ja | Nein (Transient) | **Ja (Echt)** |
|
||||
| **Max. Größe** | ~5-10 MB | GB-Bereich | RAM-abhängig | **GB-Bereich** |
|
||||
| **Zugriff** | Synchron (Blockierend) | Asynchron (Event-basiert) | Synchron (JS Thread) | **Synchron (in Worker)** |
|
||||
| **Performance** | Langsam | Mittel | Schnell (kleine Daten) | **Nahe Nativ** |
|
||||
| **Relational?** | Nein (Key-Value) | Nein (Object Store) | Ja | **Ja** |
|
||||
| **KMP Eignung** | Gering | Mittel | Mittel | **Hoch (via SQLDelight)** |
|
||||
|
||||
**Tabelle 2: SQLDelight Konfigurations-Auswirkungen**
|
||||
|
||||
| Einstellung | `generateAsync = false` (Standard) | `generateAsync = true` (Erforderlich) |
|
||||
| :--- | :--- | :--- |
|
||||
| **Query Return Type** | `T` (z.B. `List<Task>`) | `suspend () -> T` |
|
||||
| **Driver Interface** | `SqlDriver` | `SqlDriver` (Async Methoden) |
|
||||
| **JVM Verhalten** | Blockierend | Blockierend (innerhalb Suspend) |
|
||||
| **JS/Wasm Verhalten** | Nicht unterstützt (für OPFS) | Unterstützt (Promise-basiert) |
|
||||
| **Komplexität** | Niedrig | Mittel-Hoch |
|
||||
|
||||
Diese Analyse bestätigt, dass Ihr angestrebter Stack nicht nur möglich, sondern die derzeit leistungsfähigste Konfiguration für Cross-Platform-Persistenz ist.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Architecture & Build Status Report
|
||||
**Datum:** 09.01.2026
|
||||
**Von:** Senior Backend Developer
|
||||
**An:** Lead Software Architect
|
||||
|
||||
## 1. Executive Summary
|
||||
Wir haben eine umfassende Stabilisierung der Projekt-Architektur durchgeführt. Kritische Versionskonflikte im Backend (Spring Boot vs. Spring Cloud) wurden behoben. Die Trennung zwischen Frontend (KMP) und Backend (JVM) wurde durch Refactoring des `core`-Bereichs strikt durchgesetzt, um "Pollution" durch JVM-Code im Frontend zu verhindern.
|
||||
|
||||
Der Build-Prozess ist derzeit noch durch spezifische **Kotlin/Wasm Kompilierungsfehler** blockiert, die aus der strikten Typisierung und dem JS-Interop von WebAssembly resultieren.
|
||||
|
||||
---
|
||||
|
||||
## 2. Durchgeführte Maßnahmen
|
||||
|
||||
### 2.1 Backend Architecture Alignment
|
||||
* **Spring Cloud Konflikt gelöst:** Downgrade von `2025.1.0` (Oakwood, inkompatibel mit Boot 3.5) auf **`2025.0.1` (Northfields)**. Dies verhindert garantierte Laufzeitfehler (`NoSuchMethodError`).
|
||||
* **Java 25 Optimierung:** Upgrade von Micrometer auf `1.16.1` für besseren Virtual Thread Support.
|
||||
* **Exposed Versionierung:** Bestätigung der Nutzung von `1.0.0-rc-4` (statt der veralteten 0.61.0).
|
||||
|
||||
### 2.2 Modul-Hygiene & KMP Trennung
|
||||
* **Refactoring `core:core-utils`:**
|
||||
* Das Modul enthielt JVM-spezifischen Code (`DatabaseUtils.kt` mit Exposed-Abhängigkeiten), der den Frontend-Build (JS/Wasm) brach.
|
||||
* **Lösung:** Erstellung eines neuen Moduls **`:backend:infrastructure:persistence`**. Der DB-Code wurde dorthin verschoben. `core:core-utils` ist nun ein reines KMP-Modul.
|
||||
* **Zirkuläre Abhängigkeiten aufgelöst:**
|
||||
* Das Modul `frontend:shared` hatte Abhängigkeiten zu Feature-Modulen und dem Design-System, was zu Zyklen führte.
|
||||
* **Lösung:** `frontend:shared` wurde bereinigt und dient nun rein als Basis-Layer (Config, Utils).
|
||||
|
||||
### 2.3 Build-System (Gradle & KMP)
|
||||
* **Wasm-Target Konsolidierung:**
|
||||
* Um Inkonsistenzen bei der Dependency Resolution zu beheben, wurde das Target `wasmJs` **projektweit** in allen relevanten KMP-Modulen (`core`, `frontend`) aktiviert.
|
||||
* Dies löste die `Unresolved platforms: [wasmJs]` Fehler.
|
||||
|
||||
---
|
||||
|
||||
## 3. Aktuelle Blocker (Wasm Compiler)
|
||||
|
||||
Obwohl die Dependency-Struktur nun sauber ist, scheitert der Compiler im `wasmJs` Target an spezifischen Interop-Problemen:
|
||||
|
||||
1. **Fehlende Referenzen (`Unresolved reference`):**
|
||||
* `org.w3c.dom.Worker` und `kotlinx.browser.window` werden im Wasm-Kontext nicht gefunden.
|
||||
* *Ursache:* Kotlin/Wasm benötigt möglicherweise explizite Imports oder externe Deklarationen für bestimmte DOM-APIs, die in Kotlin/JS implizit waren, oder die Standard-Bibliothek wird nicht korrekt eingebunden.
|
||||
2. **JS-Interop Einschränkungen:**
|
||||
* Fehler: `Type 'ERROR CLASS: Symbol not found for Worker' cannot be used as return type`.
|
||||
* Kotlin/Wasm erlaubt keine komplexen `js("...")` Blöcke innerhalb von Funktionen und hat keinen `dynamic` Typ. Unsere ersten Fixes (Helper-Funktionen) waren ein Schritt in die richtige Richtung, aber die Typen (wie `Worker`) müssen dem Compiler bekannt gemacht werden.
|
||||
|
||||
---
|
||||
|
||||
## 4. Nächste Schritte (Plan)
|
||||
|
||||
1. **Wasm-Build reparieren:**
|
||||
* Prüfen, ob wir eine explizite Dependency (z.B. `kotlinx-browser` oder `kotlin-stdlib-wasm-js`) benötigen.
|
||||
* Falls `Worker` in der Wasm-Stdlib fehlt: Definition einer `external class Worker` für Wasm erstellen, um dem Compiler den Typ bekannt zu machen.
|
||||
2. **Backend-Verifikation ("Bauplan"):**
|
||||
* Sobald der Build durchläuft (oder wir das Frontend temporär exkludieren), werde ich den **`ping-service`** starten.
|
||||
* Ziel: Nachweis, dass Spring Context, Datenbank-Verbindung (JPA) und die neue Modul-Struktur (`backend:infrastructure:persistence`) zur Laufzeit funktionieren.
|
||||
3. **Sync-Strategie:**
|
||||
* Anschließend widmen wir uns der im Frontend-Report erwähnten "Offline-Sync"-Logik (basierend auf UUIDv7).
|
||||
|
||||
**Empfehlung:** Wir sollten den Wasm-Build-Fix priorisieren, da er aktuell das gesamte Projekt blockiert ("Fail Fast").
|
||||
@@ -0,0 +1,64 @@
|
||||
# Frontend Status Report & Architecture Update
|
||||
**Datum:** 08.01.2026
|
||||
**Von:** Senior Frontend Developer (KMP/Compose)
|
||||
**An:** Lead Software Architect
|
||||
|
||||
## 1. Executive Summary
|
||||
Das Frontend-System ("Meldestelle Portal") wurde erfolgreich auf eine moderne, zukunftssichere **Kotlin Multiplatform (KMP)** Architektur migriert, die echte Offline-Fähigkeit im Web (via Wasm/JS) und Desktop (JVM) unterstützt.
|
||||
|
||||
Kritische Blockaden im Build-System (Room-Inkompatibilität mit JS) wurden durch einen strategischen Wechsel zu **SQLDelight** gelöst. Das Dependency-Management wurde zentralisiert und bereinigt.
|
||||
|
||||
**Status:** Frontend-Build ist stabil (nach Fixes in `core-utils`). Backend-Anpassungen sind erforderlich.
|
||||
|
||||
---
|
||||
|
||||
## 2. Durchgeführte Maßnahmen
|
||||
|
||||
### 2.1 Architektur & Persistenz (Offline-First)
|
||||
* **Problem:** Die ursprünglich geplante Nutzung von **Room** erwies sich als Blocker für die Web-Targets (JS/Wasm), da Room derzeit nur JVM/Android/Native unterstützt.
|
||||
* **Lösung:** Migration zu **SQLDelight 2.2.1**.
|
||||
* Ermöglicht echte Cross-Platform-Persistenz.
|
||||
* **Web (JS/Wasm):** Nutzung von `WebWorkerDriver` in Kombination mit **OPFS (Origin Private File System)** für performante, persistente Speicherung im Browser.
|
||||
* **Desktop (JVM):** Nutzung von `JdbcSqliteDriver` mit Java 25 Virtual Threads für nicht-blockierende I/O.
|
||||
* **Async-First:** Die Datenbank-Schnittstellen wurden auf `suspend` (asynchron) umgestellt (`generateAsync = true`), um die strikten Anforderungen des Browsers zu erfüllen.
|
||||
|
||||
### 2.2 Build-System & Dependency Management
|
||||
* **Single Source of Truth:** Die `libs.versions.toml` wurde komplett refaktoriert.
|
||||
* Klare Trennung zwischen **Frontend (KMP)** und **Backend (Spring/Infra)** Dependencies.
|
||||
* Einführung von **Bundles** (`kmp-common`, `ktor-client-common`, `compose-common`) zur massiven Reduktion von Boilerplate in den `build.gradle.kts` Dateien.
|
||||
* **Wasm-Support:** Fehlende Plattform-Konfigurationen (`PlatformConfig.wasmJs.kt`) wurden ergänzt.
|
||||
* **Bereinigung:** Veraltete und nicht genutzte Dependencies wurden entfernt.
|
||||
|
||||
### 2.3 Infrastruktur & Web-Integration
|
||||
* **Webpack Konfiguration:** Hinzufügen von `opfs-headers.js`, um die notwendigen HTTP-Header (`Cross-Origin-Opener-Policy`, `Cross-Origin-Embedder-Policy`) für `SharedArrayBuffer` und OPFS im Dev-Server zu setzen.
|
||||
* **Web Worker:** Implementierung eines Custom Workers (`sqlite.worker.js`), der die Datenbank im Hintergrund-Thread verwaltet.
|
||||
|
||||
---
|
||||
|
||||
## 3. Auswirkungen auf das Backend & Core (Action Required)
|
||||
|
||||
Während der Frontend-Optimierung wurden Inkonsistenzen im Shared-Code (`core:core-utils`) aufgedeckt, die das Backend betreffen.
|
||||
|
||||
### 3.1 `core:core-utils` Kontamination
|
||||
* **Problem:** Das Modul `core:core-utils` ist als KMP-Modul konfiguriert, enthielt aber JVM-spezifischen Code für **Exposed** (JDBC-basiertes ORM), der im Frontend (JS/Wasm) nicht kompilierbar ist.
|
||||
* **Temporäre Lösung:** Die Datei `DatabaseUtils.kt` (Exposed-Helper) wurde **auskommentiert**, um den Frontend-Build zu retten.
|
||||
* **TODO Backend:**
|
||||
1. Prüfen, ob `DatabaseUtils.kt` im Backend essenziell ist.
|
||||
2. Falls ja: Verschieben in ein reines Backend-Modul (z.B. `:backend:common` oder `:backend:infrastructure:persistence`).
|
||||
3. `core:core-utils` muss "rein" bleiben (nur KMP-kompatibler Code), wenn es vom Frontend konsumiert werden soll.
|
||||
|
||||
### 3.2 API-Verträge
|
||||
* Durch den Wechsel auf SQLDelight und die Async-First Architektur im Frontend ändert sich nichts an der REST-API, aber die Erwartungshaltung an die Synchronisation (Sync-Logik) wird wichtiger.
|
||||
|
||||
---
|
||||
|
||||
## 4. Nächste Schritte (Frontend Roadmap)
|
||||
|
||||
1. **Navigation:** Implementierung einer robusten Navigationslösung (Type-Safe, Deep-Linking-fähig), die sowohl Desktop als auch Web (Browser-History) sauber bedient.
|
||||
2. **Sync-Logik:** Implementierung des Datenaustauschs zwischen lokaler SQLDelight-DB und Backend-APIs (Offline-Sync).
|
||||
3. **UI-Integration:** Anbindung der neuen asynchronen Datenbank-Repositories an die Compose-ViewModels (`Flow` -> `State`).
|
||||
|
||||
---
|
||||
|
||||
**Empfehlung an Lead Architect:**
|
||||
Bitte weisen Sie den **Senior Backend Developer** an, das Modul `core:core-utils` zu bereinigen und die auskommentierte Datenbank-Logik in ein geeignetes Backend-Modul zu migrieren. Das Frontend ist nun stabil und bereit für die Feature-Entwicklung.
|
||||
@@ -0,0 +1,219 @@
|
||||
Hier ist der vollständige Bericht im Roh-Markdown-Format, wie gewünscht:
|
||||
|
||||
# Validierung der Enterprise-Architektur: Umfassende Kompatibilitätsanalyse und Strategische Bewertung des Technologie-Stacks 2026
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Der vorliegende Forschungsbericht analysiert den vom Auftraggeber spezifizierten Technologie-Stack mit Stand vom **7. Januar 2026**. Die Architektur repräsentiert eine ambitionierte, hybride Plattformstrategie, die modernste JVM-Paradigmen (Java 25, Spring Boot 3.5) mit einer aggressiven Kotlin-Multiplatform-Strategie (Kotlin 2.3.0, Compose Multiplatform, Ktor) vereint. Das Ziel dieser Untersuchung ist die Validierung der Interoperabilität der Einzelkomponenten, die Identifikation kritischer Versionskonflikte sowie die Erstellung einer stabilisierten Kompatibilitätsmatrix („Best Compatibility List“).
|
||||
|
||||
Die Analyse deckt eine **kritische Diskrepanz** im Kern der Backend-Architektur auf: Die Kombination von Spring Boot 3.5.9 mit Spring Cloud 2025.1.0 ist technisch nicht tragfähig und führt zu unvermeidbaren Laufzeitfehlern, da der Release Train "Oakwood" (2025.1.x) exklusiv für die Spring Boot Generation 4.0 konzipiert wurde. Des Weiteren identifiziert der Bericht signifikante Synchronisationsrisiken zwischen der sehr neuen Sprachebene (Kotlin 2.3.0, veröffentlicht Dez. 2025) und dem UI-Framework (Compose Multiplatform 1.9.3), welche die Stabilität von Produktions-Builds gefährden.
|
||||
|
||||
Trotz dieser spezifischen Konflikte bestätigt die Untersuchung, dass der gewählte Stack grundsätzlich eine zukunftsweisende „State-of-the-Art“-Architektur darstellt, die durch die Nutzung von Java 25 (LTS) und Kotlin 2.3.0 massive Vorteile in Performance (Compact Object Headers, Virtual Threads) und Entwicklerproduktivität (K2 Compiler) bietet, sofern die im Bericht detaillierten Korrekturen implementiert werden.
|
||||
|
||||
---
|
||||
|
||||
## 1. Fundamentalanalyse: Laufzeitumgebung und Sprachebene
|
||||
|
||||
Das Fundament der Architektur bilden Java 25 als Long-Term-Support (LTS) Release und Kotlin 2.3.0. Diese Kombination definiert den technologischen Horizont für die Jahre 2026 bis 2030. Die Synchronisation dieser beiden Komponenten ist entscheidend für den Erfolg aller darauf aufbauenden Frameworks.
|
||||
|
||||
### 1.1 Java 25 (LTS): Implikationen für die Enterprise-Architektur
|
||||
|
||||
Java 25, veröffentlicht am 16. September 2025, markiert einen signifikanten Meilenstein in der Evolution der Java-Plattform. Als LTS-Release bietet es die notwendige Planungssicherheit für Enterprise-Projekte, bringt jedoch auch tiefgreifende Änderungen in der Speicherverwaltung und Thread-Modellierung mit sich, die direkten Einfluss auf die im Stack verwendeten Frameworks (Spring Boot, Exposed) haben.
|
||||
|
||||
#### Architektur-Treiber: Compact Object Headers und Loom-Integration
|
||||
|
||||
Die Validierung zeigt, dass Java 25 insbesondere durch die Finalisierung der **Compact Object Headers** (JEP 519) massive Vorteile für die Speichereffizienz von Spring-Boot-Anwendungen bietet. Durch die Reduktion des Objekt-Headers von 128 Bit auf 64 Bit (in 64-Bit-Umgebungen) sinkt der Heap-Verbrauch von objektintensiven Anwendungen signifikant. Dies ist für den vorliegenden Stack besonders relevant, da die Nutzung von ORM-Frameworks wie **Exposed** und **Room** typischerweise eine hohe Anzahl an kleinen Datenobjekten erzeugt.
|
||||
|
||||
Ein weiterer kritischer Aspekt ist die volle Integration von Project Loom (Virtual Threads). Spring Boot 3.5.9 ist darauf optimiert, blockierende I/O-Operationen – wie sie bei JDBC-Zugriffen via Exposed auftreten – transparent auf Virtual Threads abzubilden. Dies eliminiert die Notwendigkeit für komplexe reaktive Ketten (R2DBC) in vielen Standardszenarien, sofern die zugrundeliegende Datenbank-Treiber-Schicht (JDBC) kompatibel ist. Die Analyse bestätigt, dass die aktuellen JDBC-Treiber im Java 25 Ökosystem diese Kompatibilität gewährleisten.
|
||||
|
||||
### 1.2 Kotlin 2.3.0: Der K2-Compiler als Standard
|
||||
|
||||
Mit dem Release vom 16. Dezember 2025 festigt Kotlin 2.3.0 die Rolle des K2-Compilers als unverzichtbares Werkzeug. Für den vorliegenden Stack ergeben sich hieraus spezifische Herausforderungen und Chancen.
|
||||
|
||||
#### Interoperabilität und Compiler-Strenge
|
||||
|
||||
Kotlin 2.3.0 führt striktere Checks ein, insbesondere den **Unused Return Value Checker**. Dies hat direkte Auswirkungen auf die Code-Qualität im Bereich der Datenbank-Transaktionen (Exposed) und HTTP-Requests (Ktor). Wo früher ignorierte Rückgabewerte (z.B. der Status eines Insert-Statements oder ein HTTP-Response-Code) zu stillen Fehlern führten, erzwingt der Compiler nun eine explizite Behandlung.
|
||||
|
||||
Ein Risiko besteht in der binären Kompatibilität von Bibliotheken, die mit älteren Kotlin-Versionen (vor 2.0) kompiliert wurden. Die Analyse der `libs.versions.toml` vom Juli 2025 deutet darauf hin, dass viele Bibliotheksversionen vor dem Release von Kotlin 2.3.0 definiert wurden. Während Kotlin eine hohe Abwärtskompatibilität garantiert, können Compiler-Plugins (KSP, Compose Compiler) hier Ausnahmen bilden. Insbesondere die Interaktion zwischen Kotlin 2.3.0 und dem **Compose Compiler** erfordert eine exakte Versionierung, da Diskrepanzen hier zu Build-Abbrüchen führen.
|
||||
|
||||
#### Swift Export und KMP-Strategie
|
||||
|
||||
Für den Multiplatform-Teil des Stacks ist Kotlin 2.3.0 essenziell, da es signifikante Verbesserungen im **Swift Export** mitbringt. Die Unterstützung für native Enum-Klassen und variadische Parameter in Swift reduziert den Bedarf an Boilerplate-Code im iOS-Shared-Layer drastisch. Dies validiert die Entscheidung für Kotlin 2.3.0 als strategisch korrekt, sofern die genutzten KMP-Bibliotheken (Ktor, SQLDelight/Room) diese neuen Interop-Features unterstützen.
|
||||
|
||||
---
|
||||
|
||||
## 2. Das Spring-Ökosystem: Analyse des kritischen Versionskonflikts
|
||||
|
||||
Im Bereich der serverseitigen Architektur deckt die Analyse den schwerwiegendsten Konflikt des vorgeschlagenen Stacks auf. Die Annahme, dass eine höhere Versionsnummer (2025.1.0) automatisch Kompatibilität mit der aktuellsten Spring Boot Version (3.5.9) bedeutet, ist in diesem Fall inkorrekt und fatal für die Laufzeitstabilität.
|
||||
|
||||
### 2.1 Spring Boot 3.5.9: Stabilität vor dem Major-Sprung
|
||||
|
||||
Spring Boot 3.5.9 (Released 18. Dezember 2025) ist ein hochstabiles Maintenance-Release innerhalb der 3.x-Generation. Es basiert auf Spring Framework 6.2.x und Jakarta EE 10. Die Analyse bestätigt, dass diese Version vollständig kompatibel mit Java 25 ist und von dessen LTS-Features profitiert.
|
||||
|
||||
### 2.2 Die Inkompatibilität von Spring Cloud 2025.1.0 (Oakwood)
|
||||
|
||||
Der Nutzer plant den Einsatz von **Spring Cloud 2025.1.0**. Die detaillierte Prüfung der Spring Cloud Release Trains offenbart jedoch folgende Matrix:
|
||||
|
||||
| Release Train | Codename | Benötigte Spring Boot Basis | Spring Framework Basis | Status im Stack |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **2025.1.x** | **Oakwood** | **Spring Boot 4.0.x** | Spring Framework 7.x | **INKOMPATIBEL** |
|
||||
| **2025.0.x** | **Northfields** | **Spring Boot 3.5.x** | Spring Framework 6.2.x | **KOMPATIBEL** |
|
||||
| 2024.0.x | Moorgate | Spring Boot 3.4.x | Spring Framework 6.2.x | VERALTET |
|
||||
|
||||
**Technische Fehleranalyse:**
|
||||
Der Release Train "Oakwood" (2025.1.0) wurde entwickelt, um die nächste Generation des Spring-Ökosystems zu unterstützen (Spring Boot 4.0). Spring Boot 4.0 führt Breaking Changes ein, darunter Upgrades auf Jakarta EE 11 und Spring Framework 7.
|
||||
Wird Spring Cloud 2025.1.0 in eine Spring Boot 3.5.9 Anwendung eingebunden, treten massive Classpath-Konflikte auf. Spring Cloud Oakwood erwartet Klassen und Methoden aus Spring Framework 7, die in der Runtime von Boot 3.5 (Spring Framework 6.2) nicht existieren. Typische Fehlerbilder wären `java.lang.NoSuchMethodError`, `java.lang.ClassNotFoundException` oder `java.lang.NoClassDefFoundError` bereits während der Initialisierung des Application Contexts.
|
||||
|
||||
**Strategische Korrektur:**
|
||||
Um die Integrität des Stacks zu wahren, ist ein Downgrade des Spring Cloud Release Trains auf **2025.0.1 (Northfields)** zwingend erforderlich. Dieser Release Train wurde zeitgleich mit Spring Boot 3.5.x entwickelt und gewährleistet volle API-Kompatibilität sowie die korrekte Einbindung der Micrometer-Tracing-Bibliotheken (Version 1.15/1.16), die im Stack ebenfalls eine Rolle spielen.
|
||||
|
||||
### 2.3 Micrometer und Observability Integration
|
||||
|
||||
Im Kontext von Spring Boot 3.5.9 und Spring Cloud 2025.0.1 spielt Micrometer eine zentrale Rolle für Metrics und Tracing. Die Analyse zeigt, dass Spring Boot 3.5.9 standardmäßig **Micrometer 1.15.0** verwaltet. Es gibt jedoch Hinweise auf die Verfügbarkeit von **Micrometer 1.16.1**, welches verbesserte Support-Funktionen für Java 25 bietet.
|
||||
Obwohl Spring Boot Dependency Management stabile Versionen vorgibt, ist es in diesem High-Performance-Setup ratsam, die Micrometer-Version explizit auf 1.16.1 zu heben, um von den neuesten Optimierungen im Bereich Virtual Thread Monitoring zu profitieren, die in 1.15 noch experimentell waren.
|
||||
|
||||
---
|
||||
|
||||
## 3. Multiplatform UI Architektur: Compose und die Compiler-Falle
|
||||
|
||||
Der Bereich "Compose Multiplatform" (CMP) ist im Januar 2026 einem schnellen Wandel unterworfen. Die Kombination von CMP 1.9.3 mit Kotlin 2.3.0 stellt ein signifikantes Risiko dar, das ohne manuelle Eingriffe zum Scheitern des Build-Prozesses führen kann.
|
||||
|
||||
### 3.1 Die Diskrepanz zwischen CMP 1.9.3 und Kotlin 2.3.0
|
||||
|
||||
Compose Multiplatform 1.9.3 basiert auf Jetpack Compose 1.9.4. Kotlin 2.3.0 führt jedoch neue Anforderungen an den Compose Compiler ein, um erweiterte Debugging-Features zu unterstützen – konkret das Mapping von Stack-Traces in minifizierten (R8/ProGuard) Builds.
|
||||
|
||||
**Das technische Problem:**
|
||||
Kotlin 2.3.0 erwartet standardmäßig eine Compose Runtime der Version **1.10.0** oder höher, um die neuen "Group Key" Stack-Traces zu generieren. Wird CMP 1.9.3 verwendet, kommt es zu einer Version-Mismatch. Der in Kotlin 2.3.0 integrierte Compose Compiler versucht, Bytecode für Features zu generieren, die in der älteren Runtime (1.9.4) noch nicht vorhanden sind.
|
||||
Zusätzlich gibt es dokumentierte Fälle, in denen die Kombination aus neuem Android-Gradle-Plugin (AGP), Kotlin 2.3.0 und älteren Compose-Versionen zu Compiler-Crashes (`NotSerializableException` im Daemon) führt, da Inline-Methoden wie `CompositionLocal.getCurrent` nicht korrekt aufgelöst werden.
|
||||
|
||||
### 3.2 Strategische Lösung: Upgrade auf CMP 1.10.0
|
||||
|
||||
Zum Zeitpunkt dieses Berichts (Januar 2026) ist **Compose Multiplatform 1.10.0** (bzw. ein stabiler Release Candidate) verfügbar. Ein Upgrade auf diese Version ist nicht nur empfohlen, sondern für einen stabilen Betrieb mit Kotlin 2.3.0 faktisch notwendig.
|
||||
|
||||
**Vorteile von CMP 1.10.0 im Kontext des Stacks:**
|
||||
|
||||
1. **Vollständige Kotlin 2.3.0 Kompatibilität:** Eliminiert Compiler-Crashes und ermöglicht die Nutzung der neuen R8-Stack-Trace-Mappings für besseres Production-Debugging auf Android.
|
||||
2. **WebAssembly (Wasm) Reife:** CMP 1.10.0 hebt den Wasm-Support auf ein neues Level (Beta/Stable), was für die "Web"-Komponente des KMP-Stacks essenziell ist. Ältere Versionen (1.9.x) hatten signifikante Einschränkungen bei der Performance und dem Ressourcen-Management im Browser.
|
||||
3. **Navigation 3 Integration:** CMP 1.10.0 bündelt die stabilen Artefakte von `androidx.navigation3`, was eine vereinheitlichte Navigation über alle Plattformen (inkl. Non-Android) ermöglicht und externe Bibliotheken wie Voyager potenziell obsolet macht.
|
||||
|
||||
---
|
||||
|
||||
## 4. Middleware Analyse: Ktor und Koin
|
||||
|
||||
### 4.1 Ktor: Synchronisation der Versionen
|
||||
|
||||
Der Nutzer schlägt **Ktor 3.3.3** vor. Diese Version wurde im November 2025 veröffentlicht und basiert auf Kotlin 2.2.20.
|
||||
Die Analyse zeigt, dass **Ktor 3.4.0** (released im Dezember 2025 parallel zu Kotlin 2.3.0) die korrekte Zielversion für diesen Stack ist.
|
||||
|
||||
**Gründe für das Upgrade auf Ktor 3.4.0:**
|
||||
|
||||
* **Kotlin 2.3.0 Alignment:** Ktor 3.4.0 wurde explizit gegen Kotlin 2.3.0 kompiliert. Dies verhindert Probleme mit `kotlinx-io` Abhängigkeiten, die in Kotlin 2.3.0 aktualisiert wurden.
|
||||
* **Fix für iOS SSE:** Ktor 3.3.x leidet unter einem bekannten Bug im Darwin-Engine (iOS), bei dem Server-Sent Events (SSE) Verbindungen einfrieren können. Ktor 3.4.0 behebt dieses Problem, was für die Zuverlässigkeit der mobilen Clients im Stack entscheidend ist.
|
||||
|
||||
**Ktorfit Integration:**
|
||||
Falls Bibliotheken wie **Ktorfit** (für Retrofit-ähnliche APIs) genutzt werden, ist zu beachten, dass Ktorfit sehr empfindlich auf KSP-Versionen reagiert. Ktorfit 2.7.1 ist kompatibel mit Ktor 3.3.3. Für Ktor 3.4.0 und Kotlin 2.3.0 wird ein entsprechendes Update (z.B. Ktorfit 2.8.0) benötigt, welches die `compilerPluginVersion` korrekt auf 2.3.x setzt.
|
||||
|
||||
### 4.2 Koin 4.1.1: Dependency Injection Stabilität
|
||||
|
||||
Koin 4.1.1 erweist sich als stabile Wahl. Das Framework hat sich erfolgreich an die KMP-Architektur angepasst.
|
||||
Ein wichtiger Aspekt für 2026 ist die **Koin Annotations** Unterstützung. Mit Kotlin 2.3.0 und KSP 2.3.0 muss sichergestellt werden, dass auch die Koin-Annotations-Bibliothek (Version 2.x) aktualisiert wird, um die Generierung der Modul-Definitionen nicht zu brechen. Koin 4.1.1 selbst ist kompatibel, profitiert aber von Performance-Optimierungen in der Graph-Auflösung, die für komplexe Enterprise-Apps wichtig sind.
|
||||
|
||||
---
|
||||
|
||||
## 5. Persistenz-Layer: Konsolidierung der "Drei-Datenbanken-Strategie"
|
||||
|
||||
Der Stack listet **Exposed 0.61.0**, **SQLDelight 2.2.1** und **Room 2.8.4**. Diese Koexistenz von drei unterschiedlichen Datenbank-Frameworks ist architektonisch auffällig und deutet auf Redundanzen hin.
|
||||
|
||||
### 5.1 Serverseitige Persistenz: Exposed 0.61.0
|
||||
|
||||
**Status:** Veraltet (Release April 2025).
|
||||
**Risiko:** Hoch.
|
||||
|
||||
Die Verwendung von Exposed 0.61.0 in einem Kotlin 2.3.0 Umfeld ist hochriskant. Exposed nutzt intern starkes Inlining und reified Types. Da Kotlin 2.3.0 Änderungen an der Bytecode-Generierung und den Standardbibliotheken (z.B. `kotlinx-datetime`) vorgenommen hat, führt eine veraltete Exposed-Version fast sicher zu `NoSuchMethodError` oder Inkompatibilitäten bei Datumsformaten.
|
||||
|
||||
**Empfehlung:** Es muss zwingend auf eine neuere Version (z.B. **0.62.0+** oder ein Snapshot vom Dez 2025) aktualisiert werden, die explizit gegen Kotlin 2.2 oder 2.3 gebaut wurde. Falls keine stabile Version verfügbar ist, muss der Server-Teil temporär auf Kotlin 2.2 gepinnt werden, was jedoch den Rest des Stacks ausbremst.
|
||||
|
||||
### 5.2 Client-Seite: Room vs. SQLDelight
|
||||
|
||||
Im KMP-Bereich (Android/iOS) konkurrieren **Room 2.8.4** und **SQLDelight 2.2.1**.
|
||||
|
||||
* **Room 2.8.4:** Google hat Room im Jahr 2025 erfolgreich zu einer echten KMP-Lösung transformiert (Android, iOS, JVM, Native). Es nutzt einen gebündelten SQLite-Treiber, was konsistentes Verhalten über Plattformen hinweg garantiert.
|
||||
* **SQLDelight 2.2.1:** Der traditionelle Platzhirsch für KMP-Datenbanken.
|
||||
|
||||
**Architektonische Bewertung:**
|
||||
Die parallele Nutzung beider Frameworks bläht die App unnötig auf (zwei Compiler-Plugins, zwei Laufzeitumgebungen, erhöhte Build-Zeit).
|
||||
Da Room 2.8.4 nun vollen KMP-Support bietet und sich nahtlos in die Jetpack-Architektur (ViewModel, Paging) integriert, empfiehlt sich für Teams mit Android-Hintergrund eine **Konsolidierung auf Room**. SQLDelight ist nur dann vorzuziehen, wenn die volle Kontrolle über SQL-Statements explizit gewünscht ist oder Legacy-Code dies erzwingt. In einem "Greenfield"-Szenario sollte eines der beiden Frameworks eliminiert werden.
|
||||
|
||||
---
|
||||
|
||||
## 6. Empfohlene Kompatibilitätsmatrix (Best Compatibility List)
|
||||
|
||||
Basierend auf der Analyse der Interdependenzen zum Stichtag **Januar 2026** wird folgende bereinigte Versionsliste empfohlen. Diese Konfiguration löst den Spring-Cloud-Konflikt, synchronisiert die Kotlin-Versionen und stabilisiert den UI-Build.
|
||||
|
||||
### 6.1 Core & Backend Stack
|
||||
|
||||
| Komponente | Version (User) | **Empfohlene Version** | Status / Begründung |
|
||||
| --- | --- | --- | --- |
|
||||
| **Java SDK** | 25 | **25 (LTS)** | **Validiert.** Fundament für Performance (Loom, Compact Headers). |
|
||||
| **Kotlin** | 2.3.0 | **2.3.0** | **Validiert.** Ermöglicht Swift Export & K2 Features. |
|
||||
| **Spring Boot** | 3.5.9 | **3.5.9** | **Validiert.** Stabilste Version der 3.x Linie. |
|
||||
| **Spring Cloud** | 2025.1.0 | **2025.0.1** | **KORREKTUR ERFORDERLICH.** "Oakwood" (2025.1) benötigt Boot 4.0. "Northfields" (2025.0) ist korrekt für Boot 3.5. |
|
||||
| **Exposed** | 0.61.0 | **1.0.0-rc-4** | **Upgrade.** 0.61.0 ist zu alt (April 2025) für Kotlin 2.3. Suchen Sie nach Releases ab Q4 2025. |
|
||||
| **Micrometer** | (implizit) | **1.16.1** | **Empfohlen.** Explizites Upgrade für besseren Java 25 Support empfohlen. |
|
||||
|
||||
### 6.2 Multiplatform Client Stack
|
||||
|
||||
| Komponente | Version (User) | **Empfohlene Version** | Status / Begründung |
|
||||
| --- | --- |------------------------| --- |
|
||||
| **Compose Multiplatform** | 1.9.3 | **1.10.0-rc02** | **Upgrade.** Zwingend für volle Kotlin 2.3 Kompatibilität (R8 fixes) und Stabilität. |
|
||||
| **Ktor Client** | 3.3.3 | **3.3.3** | **Upgrade.** Aligniert mit Kotlin 2.3.0 & behebt iOS SSE Bugs. |
|
||||
| **Koin** | 4.1.1 | **4.1.1** | **Validiert.** Stabil. Prüfen auf 4.2 bei Verfügbarkeit. |
|
||||
| **Room** | 2.8.4 | **2.8.4** | **Validiert.** Exzellenter KMP Support. Empfohlen als primäre DB. |
|
||||
| **SQLDelight** | 2.2.1 | **(Entfernen)** | **Konsolidierung.** Redundant zu Room. Empfehlung: Entfernen zur Reduktion der Komplexität. |
|
||||
|
||||
### 6.3 Build-System Konfiguration (`libs.versions.toml`)
|
||||
|
||||
Die folgende Konfiguration korrigiert die Fehler der `libs.versions.toml` vom Juli 2025 und aktualisiert sie auf den Stand Januar 2026.toml
|
||||
|
||||
```
|
||||
[versions]
|
||||
# Core
|
||||
java = "25"
|
||||
kotlin = "2.3.0"
|
||||
agp = "9.0.0" # Android Gradle Plugin 9.0 ist Standard für diesen Stack
|
||||
|
||||
# Server
|
||||
springBoot = "3.5.9"
|
||||
springCloud = "2025.0.1" # KORRIGIERT von 2025.1.0 (Oakwood -> Northfields)
|
||||
exposed = "1.0.0-rc-4" # Platzhalter für aktuellste Version
|
||||
|
||||
# Client / KMP
|
||||
composeMultiplatform = "1.10.0-rc02" # UPGRADE von 1.9.3
|
||||
ktor = "3.3.3" # UPGRADE von 3.3.3
|
||||
room = "2.8.4"
|
||||
koin = "4.1.1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Migrationsstrategie und Risikomanagement
|
||||
|
||||
### 7.1 Auflösung des Spring Cloud Konflikts
|
||||
Die Migration von Spring Cloud 2025.1.0 auf 2025.0.1 ist kein Feature-Downgrade, sondern eine **Kompatibilitätskorrektur**.
|
||||
* **Aktion:** In der `build.gradle.kts` muss das `spring-cloud-dependencies` BOM ausgetauscht werden.
|
||||
* **Verifikation:** Starten Sie den Application Context. Überprüfen Sie, ob `Actuator` Endpoints erreichbar sind. Achten Sie auf Logs bezüglich `jakarta.*` Packages – Fehler hier deuten darauf hin, dass noch transitive Abhängigkeiten zu Boot 4.0/Cloud Oakwood bestehen.
|
||||
|
||||
### 7.2 Kotlin 2.3.0 & Compiler Plugins
|
||||
Der Einsatz von Kotlin 2.3.0 erfordert Disziplin bei den Compiler-Plugins.
|
||||
* **Room KSP:** Stellen Sie sicher, dass der KSP-Prozessor in der Version verwendet wird, die exakt zum Kotlin 2.3.0 Compiler passt. Eine Diskrepanz (z.B. KSP für Kotlin 2.2.20) führt zu sofortigen Build-Fehlern ("Class version mismatch").
|
||||
* **Compose Compiler:** Entfernen Sie explizite Versionsangaben für den Compose Compiler, wenn Sie das offizielle Kotlin-Plugin nutzen, da der Compiler nun gebündelt ist. Erzwingen Sie keine alte Version.
|
||||
|
||||
### 7.3 Bereinigung der Datenbank-Abhängigkeiten
|
||||
Entscheiden Sie sich strategisch für **Room** oder **SQLDelight**.
|
||||
* Wenn Sie Room wählen: Entfernen Sie das SQLDelight Gradle Plugin und alle `sqldelight`-Dependencies. Dies beschleunigt den Build signifikant, da ein kompletter Code-Generierungsschritt entfällt.
|
||||
* Wenn Sie SQLDelight wählen: Entfernen Sie die Room KSP-Prozessoren.
|
||||
|
||||
## Fazit
|
||||
|
||||
Der vorgeschlagene Technologie-Stack ist in seiner Konzeption **visionär und leistungsfähig**. Er nutzt die massiven Fortschritte von Java 25 und Kotlin 2.3.0, um eine effiziente, plattformübergreifende Architektur zu schaffen.
|
||||
Die ursprüngliche Zusammenstellung enthielt jedoch mit der **Spring Cloud Versionierung** einen fatalen Fehler und mit der **Compose/Exposed-Versionierung** signifikante Stabilitätsrisiken. Durch die Anwendung der in diesem Bericht definierten **Best Compatibility List** – insbesondere dem Wechsel auf Spring Cloud Northfields und Compose 1.10.0 – wird der Stack von einem experimentellen Zustand in eine robuste, produktionstaugliche Enterprise-Lösung überführt.
|
||||
@@ -0,0 +1,202 @@
|
||||
**An:** Senior Backend Developer
|
||||
**Von:** Lead Software Architect
|
||||
**Betreff:** Arbeitsauftrag: Implementierung des `ping-service` (Tracer Bullet)
|
||||
|
||||
Guten Tag,
|
||||
|
||||
deine nächste Aufgabe ist die Implementierung unseres ersten Microservices, des `ping-service`. Dieser Service ist von
|
||||
strategischer Bedeutung, da er als **"Tracer Bullet"** und **Blaupause** für alle zukünftigen fachlichen Services dient.
|
||||
|
||||
Mit dieser Implementierung validieren wir die gesamte Kette: von der Service-Registrierung bei Consul über das
|
||||
Gateway-Routing und die Security mit Keycloak bis hin zur Observability mit Zipkin.
|
||||
|
||||
Deine Expertise in Spring Boot, DDD und sauberer Architektur ist hier entscheidend, um eine qualitativ hochwertige und
|
||||
wiederverwendbare Vorlage zu schaffen.
|
||||
|
||||
## Deine Aufgaben im Detail:
|
||||
|
||||
1. Modulstruktur anlegen
|
||||
|
||||
Bitte lege die folgende Modulstruktur an. Beachte die neue, klarere Benennung des
|
||||
Implementierungsmoduls:
|
||||
|
||||
- `:contracts:ping-api`: Enthält die KMP-kompatiblen DTOs.
|
||||
- `:backend:services:ping:ping-service`: Enthält die Spring Boot Anwendung, Controller und Konfiguration.
|
||||
|
||||
Stelle sicher, dass die Module in der `settings.gradle.kts` registriert sind.
|
||||
|
||||
```kotlin
|
||||
include(
|
||||
":platform:platform-bom",
|
||||
":platform:platform-testing",
|
||||
":contracts:ping-api",
|
||||
":backend:services:ping:ping-service",
|
||||
":backend:infrastructure:gateway",
|
||||
// ":backend:services:registry:registry-api",
|
||||
// ":backend:services:registry:registry-domain",
|
||||
```
|
||||
|
||||
2. API-Definition in `:ping-api`
|
||||
|
||||
Definiere in `ping-api/src/commonMain/kotlin` ein einfaches, serialisierbares DTO. Dieses Modul darf **keine
|
||||
JVM-spezifischen Abhängigkeiten** enthalten, um die KMP-Kompatibilität für das Frontend zu gewährleisten.
|
||||
|
||||
```kotlin
|
||||
PingResponse.kt
|
||||
```
|
||||
```kotlin
|
||||
package de.meldestelle.api.ping
|
||||
|
||||
import kotlinx . serialization . Serializable
|
||||
|
||||
@Serializable
|
||||
data class PingResponse(
|
||||
val message: String,
|
||||
val principal: String? = null
|
||||
)
|
||||
```
|
||||
|
||||
3. Service-Implementierung in :ping-service
|
||||
|
||||
Implementiere die Spring Boot Anwendung.
|
||||
|
||||
- **`PingController.kt`:**
|
||||
- **`GET /api/ping`:** Ein öffentlicher Endpunkt, der eine `PingResponse("Pong", "anonymous")` zurückgibt.
|
||||
- **`GET /api/ping/secure`:** Ein durch Spring Security geschützter Endpunkt. Er soll den `preferred_username` aus dem `Jwt` Principal extrahieren und in der `PingResponse` zurückgeben.
|
||||
|
||||
Hier ist ein Implementierungsvorschlag für den Controller:
|
||||
|
||||
```kotlin
|
||||
// in backend/services/ping/ping-service/src/main/kotlin/.../PingController.kt
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/ping")
|
||||
class PingController {
|
||||
|
||||
@GetMapping
|
||||
fun pingPublic(): PingResponse {
|
||||
return PingResponse(message = "Pong", principal = "anonymous")
|
||||
}
|
||||
|
||||
@GetMapping("/secure")
|
||||
fun pingSecure(principal: Jwt): PingResponse {
|
||||
val username = principal.getClaimAsString("preferred_username")
|
||||
return PingResponse(message = "Pong (Secure)", principal = username)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Konfiguration**
|
||||
|
||||
Erstelle die `application.yml` für den Service. Sie muss die Anwendung für unsere Infrastruktur korrekt konfigurieren:
|
||||
|
||||
- **Service Name:** ping-service
|
||||
- **Service Discovery:** Registrierung bei Consul.
|
||||
- **Security:** Konfiguration als Resource Server, der JWTs vom `issuer-uri` unseres Keycloak-Containers validiert.
|
||||
- **Observability:** Actuator-Endpunkte (`health`, `info`, `prometheus`) freigeben und Tracing aktivieren.
|
||||
|
||||
```yaml
|
||||
# in backend/services/ping/ping-service/src/main/resources/application.yml
|
||||
|
||||
server:
|
||||
port: 8081 # Eindeutiger Port für den Service
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: ping-service
|
||||
|
||||
# --- Consul Discovery ---
|
||||
cloud:
|
||||
consul:
|
||||
host: consul
|
||||
port: 8500
|
||||
discovery:
|
||||
instance-id: \${spring.application.name}:\${random.value}
|
||||
health-check-path: /actuator/health
|
||||
health-check-interval: 10s
|
||||
|
||||
# --- Security (Keycloak) ---
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
jwt:
|
||||
issuer-uri: http://keycloak:8080/realms/meldestelle
|
||||
|
||||
# --- Observability ---
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "health,info,prometheus"
|
||||
tracing:
|
||||
sampling:
|
||||
probability: 1.0 # Trace every request
|
||||
|
||||
logging:
|
||||
pattern:
|
||||
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
|
||||
```
|
||||
|
||||
5. **Build-Konfiguration(`build.gradle.kts`)
|
||||
|
||||
Achte auf die korrekte und saubere Definition der Abhängigkeiten.
|
||||
|
||||
- `ping-api/build.gradle.kts`
|
||||
```kotlin
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm() // Für die Nutzung im Backend
|
||||
js(IR) { browser() } // Für die Nutzung im Frontend
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `ping-service/build.gradle.kts`
|
||||
```kotlin
|
||||
plugins {
|
||||
alias(libs.plugins.spring.boot)
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.spring)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// API-Modul einbinden
|
||||
implementation(project(":contracts:ping-api"))
|
||||
|
||||
// Unsere zentrale BOM für konsistente Versionen
|
||||
implementation(platform(project(":platform:platform-bom")))
|
||||
|
||||
// Spring Boot Starter
|
||||
implementation(libs.spring.boot.starter.web)
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
implementation(libs.spring.boot.starter.security)
|
||||
implementation(libs.spring.boot.starter.oauth2.resource.server)
|
||||
|
||||
// Spring Cloud (Consul, OpenFeign etc.)
|
||||
implementation(libs.spring.cloud.starter.consul.discovery)
|
||||
|
||||
// Test-Abhängigkeiten
|
||||
testImplementation(platform(project(":platform:platform-testing")))
|
||||
testImplementation(libs.bundles.test.spring)
|
||||
}
|
||||
```
|
||||
|
||||
## Definition of Done:
|
||||
|
||||
Der Auftrag gilt als erledigt, wenn:
|
||||
1. Die Anwendung erfolgreich startet und sich im Consul UI als `UP` registriert.
|
||||
2. Ein `GET`-Request auf `http//localhost:8081/api/ping` (über das Gateway) den Status `200 OK` und die `{"message":"Pong", "principal":"anonymous"}` zurückgibt.
|
||||
3. Ein `GET`-Request auf `http//localhost:8081/api/ping/secure` ohne Token den Status `401 Unauthorized` zurückgibt.
|
||||
4. Ein `GET`-Request auf `http//localhost:8081/api/ping/secure` mit einem gültigen Keycloak-Token deb Status `200 OK` und eine Antwort mit dem korrekten Benutzernamen zurückgibt.
|
||||
5. Die Requests in der Zipkin UI als Trace sichtbar sind.
|
||||
|
||||
Bei Fragen zur Konfiguration oder zur Architektur stehe ich dir zur Verfügung.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
# Technischer Kompatibilitäts- und Architekturbericht: Kotlin Multiplatform 2.3.0 Ökosystem
|
||||
|
||||
## 1. Strategische Einordnung und exekutive Zusammenfassung
|
||||
|
||||
Die Veröffentlichung von Kotlin 2.3.0 am 16. Dezember 2025 markiert einen signifikanten Wendepunkt in der Reifeentwicklung des Kotlin Multiplatform (KMP) Ökosystems. Für Softwarearchitekten und Entwicklungsleiter, die eine Einführung oder Migration auf diese Version planen, stellt sich die Landschaft nicht mehr als experimentelles Feld dar, sondern als eine streng typisierte, hochintegrierte Plattform, die jedoch eine präzise Orchestrierung der Versionen erfordert. Nach der fundamentalen Umstellung auf den K2-Compiler in Version 2.0.0 fokussiert sich die Version 2.3.0 auf die Stabilisierung der Sprachfeatures, die strikte Durchsetzung von Typ-Sicherheit und die Synchronisation mit der modernen Java-Welt, spezifisch Java 25.
|
||||
|
||||
Die zentrale Herausforderung bei der Adoption von Kotlin 2.3.0 liegt nicht in der Sprache selbst, sondern in der Interdependenz mit den Build-Systemen und Frameworks, die zeitgleich massive Versionssprünge vollziehen. Wir beobachten eine kritische Konvergenz dreier Hauptstränge im ersten Quartal 2026:
|
||||
|
||||
1. **Der Build-System-Shift:** Der Übergang von Gradle 8.x auf Gradle 9.0 und die damit einhergehende fundamentale Änderung im Android Gradle Plugin (AGP) 9.0, welches die separate Deklaration des Kotlin-Android-Plugins obsolet macht.
|
||||
|
||||
|
||||
2. **Der Backend-Baseline-Shift:** Die Veröffentlichung von Spring Boot 4.0, welches zwar auf einer Kotlin 2.2 Baseline basiert, aber architektonisch für die Nutzung mit Kotlin 2.3 und Java 25 vorbereitet ist.
|
||||
|
||||
|
||||
3. **Die UI-Konsolidierung:** Compose Multiplatform 1.10.0, das erstmals eine vereinheitlichte Preview-Logik und stabile Navigation (Navigation 3.0) bietet, wodurch die Fragmentierung zwischen Android- und Desktop-Entwicklung drastisch reduziert wird.
|
||||
|
||||
|
||||
|
||||
Dieser Bericht analysiert diese Abhängigkeiten tiefgehend und definiert die notwendigen Konfigurationen für eine stabile, produktive Entwicklungsumgebung. Er richtet sich an technische Entscheidungsträger, die Risiken minimieren und die Langlebigkeit ihrer KMP-Architektur sicherstellen müssen.
|
||||
|
||||
## 2. Kernsprache und Compiler-Infrastruktur
|
||||
|
||||
Das Fundament jeder KMP-Architektur im Jahr 2026 ist der K2-Compiler in der Version 2.3.0. Es ist essenziell zu verstehen, dass Kotlin 2.3.0 nicht nur neue Features bringt, sondern auch permissives Verhalten der Vergangenheit korrigiert. Dies hat direkte Auswirkungen auf die Kompatibilität von bestehendem Code und Bibliotheken.
|
||||
|
||||
### 2.1 Der K2-Compiler: Striktheit und Sicherheit
|
||||
|
||||
Mit Version 2.3.0 verlässt JetBrains endgültig die Toleranzphasen der K1-Ära. Der Compiler erzwingt nun Muster, die zuvor bestenfalls Warnungen erzeugten. Ein prominentes Beispiel hierfür ist der **Unused Return Value Checker**. In der funktionalen Programmierung und insbesondere in der asynchronen Programmierung mit Coroutinen ist der Rückgabewert oft das einzige Indiz für den Erfolg oder das Handle einer Operation (z.B. ein `Job`-Objekt). Das Ignorieren solcher Werte führte in der Vergangenheit oft zu subtilen Bugs, bei denen Hintergrundprozesse "fire-and-forget" gestartet wurden, ohne dass das aufrufende System deren Lebenszyklus kontrollierte. Der neue Checker in Kotlin 2.3.0 hebt dieses Problem auf die Ebene eines Compiler-Fehlers oder einer strikten Warnung, was die Codequalität in komplexen Multiplatform-Projekten inhärent erhöht, aber auch Refactoring in bestehenden Codebasen erzwingen kann.
|
||||
|
||||
Darüber hinaus wurde die **Kontext-sensitive Auflösung** (Context-Sensitive Resolution) überarbeitet. Dies betrifft vorwiegend DSLs (Domain Specific Languages), wie sie in Gradle-Build-Skripten (`build.gradle.kts`) oder in Compose UI-Deklarationen verwendet werden. In früheren Versionen gab es Inkonsistenzen in der Typinferenz, abhängig davon, ob ein Lambda als Top-Level-Konstrukt oder als Argument übergeben wurde. Kotlin 2.3.0 harmonisiert dieses Verhalten. Für den Entwickler bedeutet dies weniger "magische" Typfehler in komplexen UI-Hierarchien, erfordert aber unter Umständen Anpassungen in eigenen DSL-Bibliotheken, da der Compiler nun strenger auf Typ-Hierarchien achtet.
|
||||
|
||||
### 2.2 Java 25 Interoperabilität und Bytecode-Generierung
|
||||
|
||||
Ein strategisches Highlight von Kotlin 2.3.0 ist die explizite Unterstützung für **Java 25**. Während viele Android-Projekte noch auf Java 17 oder 21 basieren, ermöglicht Kotlin 2.3.0 für reine JVM-Module (z.B. im Backend mit Spring Boot oder Ktor) die Generierung von Bytecode, der die neuesten Instruktionen und Optimierungen der Java Virtual Machine 25 nutzt.
|
||||
|
||||
Dies hat weitreichende Implikationen für die Build-Pipeline. Wenn ein KMP-Projekt Java 25 Features nutzt, muss sichergestellt sein, dass nicht nur der Kompilierungs-Classpath (JDK Home), sondern auch die Runtime-Umgebung (Docker-Container, CI-Server) über eine Java 25 Runtime verfügt. Die Standard-Einstellung des Kotlin-Compilers bleibt, falls nicht anders konfiguriert, oft konservativ (z.B. 1.8 oder die Version der Gradle-Daemon-JVM), weshalb die explizite Konfiguration der `jvmToolchain` in Gradle zwingend erforderlich wird, um Diskrepanzen zwischen Entwicklungsumgebung und Produktionsumgebung zu vermeiden.
|
||||
|
||||
### 2.3 Migration und Deprecations
|
||||
|
||||
Der Übergang zu Kotlin 2.3.0 zieht harte Grenzen bezüglich veralteter Konfigurationen:
|
||||
|
||||
* **Sprachversionen:** Die Unterstützung für `-language-version 1.8` wurde komplett entfernt. Noch kritischer für Multiplatform-Projekte ist, dass `-language-version 1.9` für Nicht-JVM-Plattformen (Native, JS, Wasm) ebenfalls entfernt wurde. Das bedeutet, dass Projekte nicht mehr temporär auf einem alten Sprachlevel verharren können, um Inkompatibilitäten im Native-Code zu umgehen – der Code muss K2-konform sein.
|
||||
|
||||
|
||||
* **Bitcode:** Für iOS-Targets wurde die `embedBitcode` DSL endgültig entfernt. Dies spiegelt Apples Entscheidung wider, Bitcode in Xcode 15/16 abzuschaffen. Build-Skripte, die noch `embedBitcode` enthalten, werden unter Kotlin 2.3.0 brechen und müssen bereinigt werden.
|
||||
|
||||
|
||||
|
||||
**Tabelle 1: Kernkomponenten und Versionierung**
|
||||
|
||||
| Komponente | Version | Status | Kritische Anmerkung / Anforderung |
|
||||
| --- | --- | --- | --- |
|
||||
| **Kotlin Compiler** | **2.3.0** | Stable | Erzwingt K2-Semantik; Java 25 Support
|
||||
|
||||
|
|
||||
| **API Version** | 2.3 | Stable | |
|
||||
| **Sprachversion** | 2.3 | Stable | Support für 1.8 (alle) und 1.9 (non-JVM) entfernt
|
||||
|
||||
|
|
||||
| **JVM Target** | Bis 25 | Stable | Toolchain-Konfiguration empfohlen |
|
||||
| **IntelliJ IDEA** | 2025.3+ | Required | Plugin ist in 2025.3+ gebündelt
|
||||
|
||||
|
|
||||
|
||||
## 3. Build-System Architektur: Gradle und AGP
|
||||
|
||||
Die wohl komplexeste Abhängigkeitsmatrix bei der Einführung von Kotlin 2.3.0 betrifft das Build-System. Hier treffen die Zyklen von Gradle, dem Android Gradle Plugin (AGP) und dem Kotlin Gradle Plugin (KGP) aufeinander.
|
||||
|
||||
### 3.1 Die Gradle-Basisinfrastruktur
|
||||
|
||||
Kotlin 2.3.0 unterstützt ein breites Spektrum an Gradle-Versionen, beginnend bei 7.6.3 bis hin zum brandneuen Gradle 9.0.0. Diese Breite ist jedoch trügerisch. Für ein modernes KMP-Projekt, das 2026 gestartet wird, ist die Nutzung von Gradle-Versionen unter 8.0 nicht empfehlenswert.
|
||||
|
||||
Der Grund liegt in der Validierung der JVM-Targets. Gradle 8.0+ hat das Standardverhalten bei Inkompatibilitäten der JVM-Targets von einer bloßen Warnung (`warning`) zu einem Fehler (`error`) geändert. Dies schützt Entwickler davor, versehentlich Bibliotheken einzubinden, die mit einer neueren Java-Version kompiliert wurden als das Projekt selbst unterstützt. Da Kotlin 2.3.0 standardmäßig Java 25 unterstützt, ist diese strikte Validierung essenziell, um "Class File Version"-Fehler zur Laufzeit zu vermeiden.
|
||||
|
||||
**Empfehlung:** Setzen Sie auf **Gradle 8.11.1** oder, falls Sie die neuesten AGP-Features nutzen wollen, direkt auf **Gradle 9.0.0**. Gradle 9.0.0 setzt zwingend eine Java 17 Runtime für den Daemon voraus, was mit den Anforderungen moderner Android-Entwicklung harmoniert.
|
||||
|
||||
### 3.2 Die Zäsur: Android Gradle Plugin (AGP) 9.0
|
||||
|
||||
Mit AGP 9.0 (Release-Zeitraum März 2025, stabil im Jan 2026 verfügbar) vollzieht Google einen Paradigmenwechsel in der Integration von Kotlin.
|
||||
In Versionen vor 9.0 musste jedes Android-Modul explizit das `kotlin-android` Plugin anwenden. AGP 9.0 hingegen besitzt eine **eingebaute Laufzeitabhängigkeit** zum Kotlin Gradle Plugin.
|
||||
|
||||
Das bedeutet konkret: Wenn Sie AGP 9.0.0 verwenden, ist das explizite Anwenden von `id("org.jetbrains.kotlin.android")` in Ihren `build.gradle.kts` Dateien nicht nur redundant, sondern deprecated und wird Warnungen erzeugen. Kotlin 2.3.0 erkennt AGP 9.0 und deaktiviert die interne Logik des alten Plugins, um Konflikte zu vermeiden. Dies erfordert jedoch ein Umdenken bei der Konfiguration der Build-Skripte.
|
||||
|
||||
Für Projekte, die noch nicht bereit sind, auf die absolute "Bleeding Edge" von AGP 9.0 zu wechseln, bietet **AGP 8.13.0** den stabilsten Hafen. Diese Version ist vollständig kompatibel mit Kotlin 2.3.0, unterstützt Java 17 Toolchains und erfordert keine strukturellen Änderungen an den Plugin-Blöcken.
|
||||
|
||||
### 3.3 Kompatibilitätsmatrix Build-Tools
|
||||
|
||||
Die folgende Matrix visualisiert die getesteten und unterstützten Kombinationen für Kotlin 2.3.0.
|
||||
|
||||
Tabelle 2: Gradle & AGP Kompatibilität für Kotlin 2.3.0
|
||||
|
||||
| Android Gradle Plugin (AGP) Version | Min. Gradle Version | Max. Gradle Version | Kompatibilitäts-Status mit Kotlin 2.3.0 | Empfohlenes Szenario |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **9.0.0 / 9.1.0** (Alpha/Beta) | 9.0.0 | 9.x | **Unterstützt** (Deprecated `kotlin-android` Plugin) | Green-Field Projekte mit höchstem Modernitätsanspruch |
|
||||
| **8.13.x** | 8.7+ | 8.14 | **Voll Unterstützt** | **Produktions-Standard** für Q1 2026 |
|
||||
| **8.10.x - 8.12.x** | 8.7+ | 8.11+ | **Voll Unterstützt** | Stabile Bestandsprojekte |
|
||||
| **8.2.2 - 8.9.x** | 8.2+ | 8.9+ | **Unterstützt** | Legacy-Wartung |
|
||||
| **< 8.2.2** | - | - | **Inkompatibel** | Migration zwingend erforderlich |
|
||||
|
||||
### 3.4 Java Toolchains Konfiguration
|
||||
|
||||
Ein häufig übersehener Aspekt ist die korrekte Konfiguration der Java Toolchain. Da Kotlin 2.3.0 und AGP 8.x+ die Kompilierung von der Gradle-Daemon-JVM entkoppeln, muss die `jvmToolchain` im `kotlin`-Block definiert werden.kotlin
|
||||
|
||||
```
|
||||
// build.gradle.kts (Root oder Shared Module)
|
||||
kotlin {
|
||||
// Definiert die Java-Version für die Kotlin-Kompilierung (z.B. 17 oder 21 für Android)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
```
|
||||
|
||||
Ohne diese Definition versucht Gradle, die Java-Version des Daemons zu nutzen, was in CI/CD-Umgebungen (wo oft unterschiedliche JDKs installiert sind) zu nicht-reproduzierbaren Builds führen kann.[8]
|
||||
|
||||
## 4. UI-Framework: Compose Multiplatform (CMP)
|
||||
|
||||
Für die Entwicklung der Benutzeroberfläche in einem KMP-Projekt ist Compose Multiplatform (CMP) die Standardwahl. Hierbei ist eine wichtige Unterscheidung in der Versionierung zu treffen, die oft zu Verwirrung führt: Die Trennung zwischen **Compiler-Plugin** und **Laufzeit-Bibliothek**.
|
||||
|
||||
### 4.1 Compose Compiler: Version 2.3.0
|
||||
|
||||
Seit Kotlin 2.0.0 ist der Compose Compiler direkt in das Kotlin-Repository integriert. Das bedeutet, es gibt keine separate Versionierung mehr für den Compiler.
|
||||
* **Anforderung:** Wenn Sie Kotlin 2.3.0 verwenden, **müssen** Sie den Compose Compiler in Version 2.3.0 verwenden.
|
||||
* **Integration:** Dies geschieht über das Gradle-Plugin `org.jetbrains.kotlin.plugin.compose`. Alte Referenzen auf `androidx.compose.compiler:compiler` müssen zwingend entfernt werden, da diese Artefakte nicht mehr mit dem K2-Compiler kompatibel sind.[11]
|
||||
|
||||
### 4.2 Compose Laufzeit & UI: Version 1.10.0
|
||||
|
||||
Während der Compiler an Kotlin gebunden ist, entwickeln sich die UI-Bibliotheken (Foundation, Material, Runtime) eigenständig weiter. Für Kotlin 2.3.0 ist die kompatible Version **Compose Multiplatform 1.10.0** (veröffentlicht im Dezember 2025).[6, 12]
|
||||
|
||||
Diese Version bringt massive Verbesserungen für die Cross-Plattform-Entwicklung:
|
||||
1. **Vereinheitlichte `@Preview` Annotation:** Bis Version 1.9 mussten Entwickler unterschiedliche Annotationen für Android-Previews (`androidx...`) und Desktop-Previews (`org.jetbrains...` oder desktop-spezifisch) nutzen. CMP 1.10.0 führt eine unified Annotation in `commonMain` ein.[6] Dies reduziert Boilerplate-Code signifikant und ermöglicht es, UI-Komponenten direkt im gemeinsamen Code zu visualisieren, ohne plattformspezifische Stubs erstellen zu müssen.
|
||||
2. **Navigation 3.0:** Mit CMP 1.10.0 wird Navigation 3.0 stabil. Dies löst den veralteten `PredictiveBackHandler` ab und bietet eine typsichere, ereignisgesteuerte Navigationsstruktur, die auch Deep-Linking auf iOS und Desktop unterstützt.[6]
|
||||
3. **Web (Wasm/HTML) Reife:** Die Version 1.10.0 bringt erstmals Accessibility-Support für Web-Targets.[13] Dies ist ein entscheidender Schritt für Enterprise-Anwendungen, da Barrierefreiheit oft eine harte Anforderung für interne Tools darstellt. Zudem wurde die API zum Einbetten von HTML-Inhalten verbessert, was hybride Ansätze erleichtert.
|
||||
|
||||
**Tabelle 3: Compose Multiplatform Konfiguration**
|
||||
|
||||
| Komponente | Version | Gradle Plugin / Artefakt | Anmerkung |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Compiler Plugin** | **2.3.0** | `org.jetbrains.kotlin.plugin.compose` | Version muss exakt Kotlin-Version matchen |
|
||||
| **Gradle Plugin** | **1.10.0** | `org.jetbrains.compose` | Steuert Abhängigkeiten und Multiplatform-Tasks |
|
||||
| **Runtime Libs** | **1.10.0** | `org.jetbrains.compose.runtime:runtime` | Basiert auf Jetpack Compose 1.10/Material 1.4 |
|
||||
| **Material 3** | **1.4.0** | `org.jetbrains.compose.material3:material3` | [14] |
|
||||
|
||||
## 5. Backend-Integration: Spring Boot 4.0
|
||||
|
||||
Für KMP-Projekte, die nicht nur Clients, sondern auch Server-Komponenten umfassen (Full-Stack Kotlin), ist die Kompatibilität mit Spring Boot entscheidend.
|
||||
|
||||
### 5.1 Spring Boot 4.0 und die "Kotlin 2.2 Baseline"
|
||||
|
||||
Spring Boot 4.0 (erschienen Nov 2025) basiert offiziell auf einer **Kotlin 2.2 Baseline**.[5, 15] Dies ist eine bewusste Entscheidung des Spring-Teams, um Bibliotheksentwicklern Stabilität zu garantieren und nicht sofort auf den allerneuesten Compiler zu zwingen.
|
||||
Das bedeutet jedoch **nicht**, dass Kotlin 2.3.0 inkompatibel ist. Im Gegenteil: Spring Boot 4.0 ist darauf ausgelegt, mit neueren Kotlin-Versionen zu laufen ("forward compatibility").
|
||||
|
||||
**Integrations-Strategie:**
|
||||
Um Kotlin 2.3.0 in einem Spring Boot 4.0 Projekt zu nutzen, müssen Sie die von Spring verwaltete Kotlin-Version überschreiben. In `build.gradle.kts` geschieht dies typischerweise im `plugins`-Block oder über `ext["kotlin.version"] = "2.3.0"`. Da Kotlin eine strikte binäre Rückwärtskompatibilität pflegt, funktioniert der Spring Boot 4.0 Bytecode (kompiliert mit 2.2) problemlos mit der 2.3.0 Runtime.
|
||||
|
||||
### 5.2 Serialisierung: Jackson vs. Kotlinx.Serialization
|
||||
|
||||
Ein kritischer Punkt in Spring Boot 4.0 ist die Behandlung von JSON. Spring Boot 4.0 führt ein neues Starter-Modul ein: `spring-boot-starter-kotlinx-serialization-json`.[5]
|
||||
* **Das Problem:** Da Spring Boot 4.0 auf der 2.2 Baseline fußt, zieht dieser Starter standardmäßig eine Version von `kotlinx.serialization`, die evtl. nicht optimal für Kotlin 2.3.0 ist.
|
||||
* **Die Lösung:** Wenn Sie diesen Starter nutzen, müssen Sie sicherstellen, dass Sie explizit die `kotlinx.serialization` Version **1.10.0** erzwingen (siehe Abschnitt Bibliotheken), da diese Version mit dem Kotlin 2.3.0 Compiler-Plugin synchronisiert ist.[5]
|
||||
|
||||
## 6. Essenzielle Bibliotheken und Versionen
|
||||
|
||||
Ein KMP-Projekt steht und fällt mit der Kompatibilität seiner Kernbibliotheken. Da Kotlin 2.3.0 Änderungen im Compiler (K2) mitbringt, müssen Bibliotheken, die Compiler-Plugins nutzen oder tief in die IR (Intermediate Representation) eingreifen, aktualisiert werden.
|
||||
|
||||
### 6.1 Kotlinx.Coroutines: 1.10.2
|
||||
|
||||
Für asynchrone Programmierung ist **Version 1.10.2** die korrekte Wahl für Kotlin 2.3.0.[16]
|
||||
Diese Version beinhaltet Anpassungen an die neuen Kontext-sensitiven Auflösungen des Compilers und behebt spezifische Memory-Leaks im Native-Memory-Management, die bei der Nutzung von Flows auf iOS auftreten konnten.
|
||||
|
||||
### 6.2 Kotlinx.Serialization: 1.10.0
|
||||
|
||||
Hier ist strikte Disziplin erforderlich. Das Serialization-Plugin greift direkt in den Kompilierungsprozess ein.
|
||||
* **Regel:** Die Version des Gradle-Plugins (`kotlin("plugin.serialization")`) muss **2.3.0** sein.
|
||||
* **Regel:** Die Version der Laufzeitbibliothek (`kotlinx-serialization-json`) sollte **1.10.0** (oder der zum Release-Zeitpunkt verfügbare RC) sein.[17] Mischmasch-Versionen führen hier unweigerlich zu `java.lang.NoSuchMethodError` oder Compiler-Abstürzen.
|
||||
|
||||
### 6.3 Netzwerkschicht: Ktor & Okio
|
||||
* **Ktor:** Die Versionen **3.2.2** oder **3.3.0** sind kompatibel. Ktor 3.3.0 bringt zudem verbesserte Unterstützung für OpenAPI und WebRTC, was gut mit den neuen Web-Fähigkeiten von Compose harmoniert.
|
||||
* **Okio/AtomicFU:** Nutzen Sie AtomicFU **0.29.0** für atomare Operationen im Multiplatform-Code.[14]
|
||||
|
||||
**Tabelle 4: Empfohlener "Bill of Materials" (BOM) für Kotlin 2.3.0**
|
||||
|
||||
| Bibliothek | Empfohlene Version | Gradle Koordinaten | Grund / Abhängigkeit |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Coroutines** | 1.10.2 | `org.jetbrains.kotlinx:kotlinx-coroutines-core` | K2-Kompatibilität [16] |
|
||||
| **Serialization** | 1.10.0 | `org.jetbrains.kotlinx:kotlinx-serialization-json` | Sync mit Compiler 2.3.0 [17] |
|
||||
| **DateTime** | 0.7.1 | `org.jetbrains.kotlinx:kotlinx-datetime` | Fixes für Wasm/JS [14] |
|
||||
| **Ktor Client** | 3.2.2 / 3.3.0 | `io.ktor:ktor-client-core` | Stabile Netzwerk-Layer |
|
||||
| **SQLDelight** | 2.0.2+ | `app.cash.sqldelight` | Datenbank-Support |
|
||||
| **Koin** | 4.0.0+ | `io.insert-koin:koin-core` | Dependency Injection für KMP |
|
||||
|
||||
## 7. Migration und Projektaufbau: Eine Roadmap
|
||||
|
||||
Für den Aufbau eines neuen Projekts ("Greenfield") oder die Migration eines bestehenden ("Brownfield") ergeben sich aus den oben genannten Daten klare Handlungsempfehlungen.
|
||||
|
||||
### 7.1 Szenario A: Neues Projekt (Greenfield)
|
||||
|
||||
Starten Sie direkt mit dem modernsten Stack, um technische Schulden in 2026 zu vermeiden.
|
||||
1. **Gradle:** Setzen Sie `distributionUrl` auf **Gradle 9.0**.
|
||||
2. **AGP:** Nutzen Sie **AGP 9.0.0** (oder RC). Verzichten Sie in den Modul-Build-Files auf `id("kotlin-android")`. Der `com.android.application` Plugin-Block ist ausreichend.
|
||||
3. **Kotlin:** `version "2.3.0"` im `libs.versions.toml`.
|
||||
4. **Compose:** Plugin Version `1.10.0`.
|
||||
5. **Struktur:** Nutzen Sie die neue Projektstruktur, die Logik strikt in `commonMain` hält und plattformspezifische Ordner (`androidMain`, `iosMain`) nur für echte Interop-Fälle (z.B. Bluetooth, Kamera) verwendet.
|
||||
|
||||
### 7.2 Szenario B: Migration (Brownfield)
|
||||
|
||||
Die Migration ist risikobehafteter, insbesondere wegen der veralteten Sprachversionen.
|
||||
1. **Code-Bereinigung:** Entfernen Sie alle Referenzen auf `-language-version 1.8` oder `1.9` (für Native). Der Code muss compilieren, ohne dass diese Flags gesetzt sind.
|
||||
2. **Bitcode entfernen:** Löschen Sie alle `embedBitcode`-Blöcke aus den Gradle-Skripten.
|
||||
3. **Schrittweises Upgrade:**
|
||||
* Upgrade Gradle auf 8.11.1 (sicherer Zwischenschritt vor 9.0).
|
||||
* Upgrade AGP auf 8.13.0 (vermeidet den Plugin-DSL-Bruch von AGP 9.0 vorerst).
|
||||
* Upgrade Kotlin auf 2.3.0 und Compose auf 1.10.0.
|
||||
* Upgrade der Bibliotheken (Coroutines, Serialization).
|
||||
4. **Validierung:** Prüfen Sie Warnungen des "Unused Return Value Checkers". In vielen Fällen deckt dieser Checker logische Fehler in der Fehlerbehandlung von Netzwerk-Calls auf.
|
||||
|
||||
## 8. Fazit und Ausblick
|
||||
|
||||
Kotlin 2.3.0 ist keine einfache inkrementelle Version, sondern ein stabilisierender Meilenstein, der das Ökosystem auf die Post-K2-Ära einschwört. Die Kompatibilität ist gewährleistet, sofern man die "Regeln des Spiels" beachtet: **Synchronisation von Compiler- und Bibliotheks-Versionen**, **Beachtung der AGP-9.0-Zäsur** und **Verwendung moderner Gradle-Infrastruktur**.
|
||||
|
||||
Für Entwickler bietet die Kombination aus Kotlin 2.3.0, Compose 1.10.0 und Spring Boot 4.0 eine mächtige, durchgängig typisierte Plattform, die von der Datenbank (via Exposed/SQLDelight) über das Backend (Spring Boot/Ktor) bis hin zur UI auf iOS, Android und Web (Compose) reicht. Die Investition in die korrekte Einrichtung der Build-Skripte zu Beginn des Projekts amortisiert sich durch die massive Reduktion von "Dependency Hell"-Problemen im weiteren Projektverlauf.
|
||||
Reference in New Issue
Block a user