Files
werkbaum/backend/README.md
T
mhoennigandClaude Opus 5 f3afc8dfa8 refactor(backend): Paketwurzel de.werkbaum, Tests auf RestTestClient
Das Backend-Geruest kam unter der Platzhalter-Wurzel com.example.editor
herein und widersprach backend/CLAUDE.md. Umgezogen nach de.werkbaum: 17
Kotlin-Dateien, Gradle-group, apiPackage/modelPackage der OpenAPI-Generierung,
die jacoco-Ausschluesse und das Cucumber-glue-Paket. Jetzt war es billig, mit
jeder Woche Entwicklung waere es teurer geworden.

Die BDD-Tests nutzen jetzt RestTestClient statt TestRestTemplate, das in
Spring Boot 4 als Auslaufmodell gilt. Weil Cucumber Senden und Pruefen
trennt, wird die fluent API nicht fuer Zusicherungen genutzt, sondern ueber
returnResult das Ergebnis festgehalten.

Nachgemessen: 22 Tests gruen, check inklusive Coverage-Verifikation besteht,
91,7 % Zeilenabdeckung, generierter Code weiterhin ausgeschlossen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 15:21:34 +02:00

117 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Editor Backend Grundgerippe
CRUD-Skelett mit **Spring Boot 4**, **Kotlin** und **API First** (OpenAPI 3, YAML).
Die vier HTTP-Befehle (GET, POST, PUT, DELETE) sind für die Ressource `Document`
umgesetzt bewusst noch **ohne Autorisierung**, aber mit vorbereiteten
Erweiterungspunkten für Live-Editing, Autorisierung und clientseitige
Verschlüsselung.
## Voraussetzungen
- JDK 21
- Gradle 9 (einmalig `gradle wrapper --gradle-version 9.1` ausführen, danach `./gradlew`)
## Wichtige Kommandos
| Kommando | Zweck |
|------------------------------|------------------------------------------------------------------------------|
| `./gradlew openApiGenerate` | Generiert API-Interfaces + Modelle aus `src/main/resources/openapi/api.yaml` |
| `./gradlew build` | Generierung, Kompilierung, alle Tests, Coverage-Prüfung |
| `./gradlew test` | Unit- und Behavior-Tests (Cucumber) |
| `./gradlew jacocoTestReport` | Coverage-Report unter `build/reports/jacoco/test/html` |
| `./gradlew bootRun` | Startet das Backend auf Port 8080 |
## API First Ablauf
1. Vertrag ändern: `src/main/resources/openapi/api.yaml`
2. `./gradlew openApiGenerate` → erzeugt `DocumentsApi` (Interface) und Modelle
nach `build/generated/openapi` (Pakete `de.werkbaum.generated.*`)
3. `DocumentsController` implementiert das Interface mit
`skipDefaultInterface=true`: Weicht die Implementierung vom Vertrag ab,
**bricht der Build** Spezifikation und Code können nicht auseinanderlaufen.
Generierter Code wird nicht eingecheckt und zählt nicht zur Code Coverage.
## Teststrategie
- **Behavior-Tests (Cucumber, `src/test/resources/features/dokumente.feature`)**
testen die API von außen gegen die laufende Anwendung (`RANDOM_PORT`):
Statuscodes, Payloads, Fehlerpfade. Die Szenarien sind auf Deutsch
(`# language: de`) und dienen als lebende Dokumentation.
- **Unit-Tests (JUnit 5 + MockK)** decken die Geschäftslogik im
`DocumentService` isoliert ab (Versionierung, Zeitstempel per festem `Clock`,
Fehlerfälle).
- **Coverage**: JaCoCo, Verifikation mit mind. 80 % Line Coverage
(`jacocoTestCoverageVerification`, hängt an `check`).
## Architektur
```
api/ DocumentsController (implementiert generiertes Interface),
GlobalExceptionHandler (RFC 9457 ProblemDetail)
service/ DocumentService (Geschäftslogik, Versionierung), Clock-Bean
repository/ DocumentRepository + DocumentHistoryRepository (Interfaces)
persistence/ JPA-Entities, Spring-Data-Repositories und Adapter (H2/Liquibase)
domain/ Document (internes Modell, getrennt vom API-Modell)
```
Das interne Domänenmodell ist bewusst vom generierten API-Modell getrennt
so können API-Vertrag und Persistenz unabhängig voneinander weiterentwickelt
werden.
## Historie & Wiederherstellung
- Jede Änderung (CREATED, UPDATED, DELETED, RESTORED) wird als Snapshot in
einer vom Dokument getrennten Historie protokolliert sie **überlebt ein
DELETE**.
- `GET /api/v1/documents/{uuid}/history` liefert alle Einträge (älteste
zuerst); Identifier ist die UUID, wie bei GET (der Titel ist nicht eindeutig).
- `POST /api/v1/documents/{uuid}/restore` stellt ein gelöschtes Dokument unter
derselben UUID wieder her (letzter Stand vor dem Löschen). Mit optionalem
Body `{"version": n}` wird eine bestimmte Version wiederhergestellt das
funktioniert auch als Rollback für noch existierende Dokumente; ohne
Zielversion antwortet der Server bei existierendem Dokument mit 409.
## Vorbereitete Erweiterungen
**Autorisierung**
- `bearerAuth` (JWT) ist in der OpenAPI-Spec als Security Scheme definiert,
aber noch auf keine Operation angewendet.
- Später: `spring-boot-starter-security` + `security: [bearerAuth]` in der
Spec; die Behavior-Tests erhalten dann einen Auth-Schritt
(„Angenommen ich bin als … angemeldet").
**Live-Editing**
- Jedes Dokument trägt eine `version`, die bei jedem Update inkrementiert
wird Basis für Optimistic Locking (HTTP 409 ist in der Spec bereits
reserviert) und für Delta-Synchronisation über WebSocket/STOMP.
- `DocumentUpdateRequest.expectedVersion` ist bereits im Vertrag vorgesehen,
wird aber noch nicht ausgewertet.
**Clientseitige Verschlüsselung**
- `content` ist ein opaker String, den der Server nie interpretiert. Der
Wechsel auf Ciphertext erfordert keine API-Änderung; ggf. kommen später
Metadaten-Felder (z. B. Schlüssel-ID, Nonce) als eigene Properties hinzu.
## Persistenz
- **H2 im File-Modus** (`./data/editor.mv.db`) mit `MODE=PostgreSQL`
läuft im Server-Prozess, keine Datenbank-Installation nötig. Dokumente und
Historie überleben einen Neustart.
- **Schema per Liquibase im formatierten SQL-Format**
(`src/main/resources/db/changelog/db.changelog-master.sql`, kein XML).
Neue Änderungen werden als weitere `--changeset`-Blöcke angehängt;
Hibernate validiert nur (`ddl-auto: validate`).
- **Umstieg auf echtes PostgreSQL:** im Wesentlichen JDBC-URL/Credentials in
der `application.yaml` tauschen und den Postgres-Treiber als Dependency
ergänzen Schema-Migrationen und Code bleiben unverändert.
- Tests laufen gegen H2 in-memory (`src/test/resources/application.yaml`),
mit demselben Liquibase-Schema.
- Service-Methoden sind `@Transactional`: Dokument-Änderung und
Historieneintrag werden atomar geschrieben.
## Hinweise
- Versionsnummern in `build.gradle.kts` (Spring Boot, OpenAPI Generator,
Cucumber, MockK) beim ersten Build ggf. auf den aktuellen Patch-Stand heben.