Der Server rebased selbst: Ist die Basis veraltet, ueberschneiden sich die Operationen aber nicht mit den zwischenzeitlichen, verschiebt er sie und akzeptiert. Reines Ablehnen fuehrte zu Starvation — ein Client mit hoher Latenz kaeme bei fleissigen Mitschreibern womoeglich nie durch. 409 gibt es nur bei echter Ueberschneidung, mit allem, was der Client zum Weiterarbeiten braucht, ohne neu zu laden. Pruefsumme ist Pflicht (422 bei Abweichung): Die Versionsnummer bestaetigt nur, dass die Basis dieselbe Version ist, nicht dass beide Seiten sie gleich lesen. clientId + seq machen den Aufruf wiederholbar — im Mobilnetz ist die verlorene Antwort der Normalfall. Die Sperre je Dokument liegt ausserhalb der Transaktion: innen gaebe der Proxy sie vor dem Commit frei, und der naechste Schreiber laese einen Stand, der noch nicht steht. Deshalb ist LiveEditingService nicht transaktional und schreibt ueber DocumentService. Was das Konzept offenliess, ist jetzt entschieden und in D76-Nachtrag 4 begruendet: die Randfaelle der Einfuege-Ueberschneidung, die Trennung von 400 und 422, die gedeckelte Idempotenz im Speicher. 104 Tests, davon 8 Cucumber-Szenarien fuer das Live-Editing. Gegenprobe: Pruefsumme nicht geprueft, Idempotenz entfernt, veraltete Basis abgelehnt statt verschoben -> es fallen jeweils genau die danach benannten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
2.9 KiB
Markdown
55 lines
2.9 KiB
Markdown
# Werkbaum · Backend
|
||
|
||
Kotlin/Spring-Boot-Anwendung. Aufgaben: Persistenz der Notationstexte,
|
||
Live-Editing (D76), Taiga-Integration (REST-API, `#ref`-Auflösung,
|
||
Status-Sync), später Tenzu-Adapter.
|
||
|
||
**Stand:** Gerüst steht — Dokumenten-CRUD mit Historie und Wiederherstellung,
|
||
API-First aus `src/main/resources/openapi/api.yaml`, H2 mit Liquibase.
|
||
Kommandos in README.md hier. Live-Editing (D76,
|
||
`docs/live-editing-proposal.md`) ist in Arbeit: Schritte 1–3 der Reihenfolge
|
||
dort sind gebaut (Zeilen-Diff in `de.werkbaum.diff`, Historie in zwei Ebenen,
|
||
`PATCH /content` im `LiveEditingService`), ab Schritt 4 (`GET /changes` per
|
||
Long Polling) steht es aus.
|
||
|
||
## Konventionen
|
||
- Kotlin, **Spring Boot 4**, Gradle (Kotlin DSL), JDK 21.
|
||
- Paketwurzel `de.werkbaum`; Schichten: `api` (Controller), `domain`,
|
||
`service`, `repository` (Interfaces), `persistence` (JPA), später
|
||
`integration.taiga` (Client, Mapping).
|
||
- **API First:** Interfaces und Modelle werden aus der OpenAPI-Spezifikation
|
||
generiert; der Controller implementiert sie. Ändert sich die Spec, schlägt
|
||
der Compile fehl — genau so ist es gewollt.
|
||
- Tests mit JUnit 5 als Runner + **Kotest-Assertions** (`shouldBe`,
|
||
`shouldContain`, `shouldThrow`) und MockK; Verhalten per Cucumber gegen die
|
||
laufende Anwendung (`RestTestClient`, nicht TestRestTemplate — das ist in
|
||
Boot 4 Auslaufmodell). Taiga-Client gegen aufgezeichnete Antworten
|
||
(WireMock), nie gegen Live-Instanzen.
|
||
- Konfiguration über `application.yaml` + Umgebungsvariablen;
|
||
keine Zugangsdaten im Repository.
|
||
- Keine neuen **Laufzeit**-Abhängigkeiten ohne Rückfrage (Wurzel-CLAUDE.md);
|
||
Test-Abhängigkeiten sind unkritisch, sie landen in keinem Artefakt.
|
||
|
||
## Spring Boot 4 — drei Fallen (D13-Nachtrag)
|
||
Vieles ist aus dem Kern in eigene Module gewandert. Was uns getroffen hat:
|
||
|
||
- `spring-boot-starter-test` bringt **kein** `TestRestTemplate`/`RestTestClient`
|
||
mit — dafür `spring-boot-resttestclient`.
|
||
- `@SpringBootTest` stellt die Test-Client-Bean **nicht** mehr von selbst
|
||
bereit: `@AutoConfigureRestTestClient` gehört an die Testkonfiguration.
|
||
- `org.liquibase:liquibase-core` allein bringt die Autokonfiguration nicht
|
||
mehr mit; ohne `spring-boot-starter-liquibase` läuft keine Migration, und
|
||
der Fehler zeigt sich erst spät als „Schema validation: missing table".
|
||
|
||
## Wichtig (D14 — Parser-Hoheit)
|
||
Das Backend parst die Notation **nicht**. Es speichert den Text als Ganzes
|
||
und arbeitet mit expliziten Metadaten. Sollte Backend-Parsen doch nötig
|
||
werden: zuerst DECISIONS ergänzen, dann gegen die gemeinsamen Fixtures aus
|
||
docs/SPEC.md §10 testen — niemals eine zweite, abweichende Grammatik pflegen.
|
||
|
||
## Taiga-Mapping (Vorgabe aus docs/ROADMAP.md)
|
||
- `#123` referenziert Epic/User Story/Task/Issue; Auflösung liefert Titel,
|
||
URL, Status. Status-Mapping Taiga-Workflow → Notation konfigurierbar
|
||
(Default: „New"→`[ ]`, „In progress"→`[~]`, „Ready for test"→`[/]`,
|
||
„Done"→`[x]`, „Archived"→`[^]`).
|