# 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.
