docs: restructure and streamline sprint execution order
- Consolidated and removed redundant steps in `SPRINT_EXECUTION_ORDER.md`. - Simplified descriptions and roadmap formatting for improved clarity. - Updated progress and dependencies to align with Phase 8 objectives. - Adjusted role-specific roadmaps to reflect the latest sprint updates. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
@@ -1,177 +1,104 @@
|
||||
# 👷 [Backend Developer] — Schritt-für-Schritt Roadmap
|
||||
# 👷 [Backend Developer] — Zwischenstand & Roadmap
|
||||
|
||||
> **Stand:** 3. April 2026
|
||||
> **Rolle:** Spring Boot / Ktor, Kotlin, SQL, API-Design, Datenbankschema, Services
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Sprint A — Sofort (diese Woche)
|
||||
## ✅ Erledigte Sprints
|
||||
|
||||
> ✅ ADR-0021 (Tenant-Strategie) liegt vor (2026-04-02) — A-1 gestartet
|
||||
### Sprint A (Teilweise) — Abgeschlossene Punkte
|
||||
|
||||
- [ ] **A-1** | Tenant-Isolation im Datenzugriffs-Layer implementieren
|
||||
- [x] ADR-0021 (Architect) lesen und Strategie übernehmen
|
||||
- [x] Tenant-Resolution-Mechanismus implementieren (wie erkennt das Backend die Ziel-Datenbank?)
|
||||
- Entries Service: `TenantWebFilter` liest `X-Event-Id`/Subdomain; `TenantRegistry` (In-Memory, konfigurierbar)
|
||||
- [x] Alle Datenzugriffe mit Tenant-Kontext absichern
|
||||
- Entries Service (Exposed): `tenantTransaction {}` setzt `SET search_path TO <schema>` pro Request
|
||||
- [x] Sicherstellen: Kein Cross-Tenant-Datenzugriff möglich
|
||||
- Verhindert durch verpflichtenden Tenant-Kontext + `search_path`; Fehlerfälle: 400/404/423
|
||||
- Nächste Schritte (A-1 Ausbau):
|
||||
- [x] `JdbcTenantRegistry` gegen `control.tenants` implementieren (inkl. Migrationen)
|
||||
- [x] Flyway-SQL: `db/control/V1__init_control_and_tenants.sql`
|
||||
- [x] Spring-JDBC `JdbcTenantRegistry` + Konfiguration (`multitenancy.registry.type=jdbc`)
|
||||
- [x] Flyway pro Tenant-Schema (Rollout aktivierter Tenants)
|
||||
- [x] `db/tenant/V1__entries_schema.sql` (Tabellen `nennungen`, `nennungs_transfers`)
|
||||
- [x] `TenantMigrationsRunner` migriert aktive Schemas beim Start (liest `control.tenants` oder `multitenancy.defaultSchemas`)
|
||||
- [ ] Rollout der Absicherung auf weitere Services (Repos/DAOs)
|
||||
- [ ] Folge-PRs für weitere Services (aktuell: Entries Service migriert)
|
||||
- [x] Observability: `tenant_id` in Logs/Metrics/Traces`
|
||||
- [x] `TenantWebFilter` setzt `MDC["tenant_id"]`
|
||||
- [x] Tests: Unit (Resolver/Registry) + E2E (Isolation A/B)
|
||||
- [x] Unit: `JdbcTenantRegistryTest` (H2)
|
||||
- [x] E2E: Isolation A/B mit Testcontainers Postgres
|
||||
- [ ] Aktueller Status: Integrationstest temporär via `@Disabled` deaktiviert, um den Build zu entblocken; Re-Enable nach Stabilisierung der Jackson/Spring-Web-Konverter-Autokonfiguration
|
||||
|
||||
- [ ] **A-2** | Datenbankschema: Domänen-Hierarchie umsetzen
|
||||
- [x] Tabelle `veranstaltungen` anlegen (interne ID, Tenant-Grenze)
|
||||
- [x] Tabelle `turniere` anlegen (FK → `veranstaltung_id`, OEPS-Turniernummer als eigenes Feld)
|
||||
- [x] Tabelle `bewerbe` anlegen (FK → `turnier_id`, Klasse, Höhe, Bezeichnung)
|
||||
- [x] Tabelle `abteilungen` anlegen (FK → `bewerb_id`, `nr`, `bezeichnung`,
|
||||
`typ: SEPARATE_SIEGEREHRUNG | ORGANISATORISCH`)
|
||||
- [x] Tabelle `teilnehmer_konten` anlegen (FK → `veranstaltung_id`, aggregiert Salden über Turniere)
|
||||
- [x] Tabelle `turnier_kassa` anlegen (FK → `turnier_id`, separate Kassa pro Turnier)
|
||||
- [x] Migrations-Skript schreiben und testen (`db/tenant/V2__domain_hierarchy.sql`, Test: `DomainHierarchyMigrationTest`)
|
||||
- [x] Ergänzung V3 (Turnier-Status): Migration `db/tenant/V3__turniere_status.sql`
|
||||
- [x] `ALTER TABLE turniere ADD COLUMN status VARCHAR(16) NOT NULL DEFAULT 'DRAFT'` + CHECK(`status` IN ('DRAFT','PUBLISHED'))
|
||||
- [x] `ALTER TABLE turniere ADD COLUMN published_at TIMESTAMP WITH TIME ZONE NULL`
|
||||
- [x] Backfill: Alle bestehenden Zeilen auf `DRAFT` setzen; Default danach wieder entfernen oder beibehalten (Entscheidung: beibehalten für Insert-Sicherheit)
|
||||
- [x] Indexe: `CREATE INDEX IF NOT EXISTS idx_turniere_status ON turniere(status)`
|
||||
- [x] Folgetasks: Domänenservice-Validierung für Statuswechsel (siehe B-1 Turniere/PATCH)
|
||||
|
||||
- [ ] **A-3** | Validierungs-Grundlage: Turnierkategorie-Limits
|
||||
- [x] Grundlage implementiert: Entkoppelte Policy-Schnittstelle + Bewerb-Descriptor
|
||||
- Events-Domain: `DomTurnier.validateKategorieLimits(bewerbe, policy)` delegiert an `TurnierkategoriePolicy`
|
||||
- Neu: `TurnierBewerbDescriptor`, `TurnierkategoriePolicy`, `NoopTurnierkategoriePolicy`
|
||||
- Test: `DomTurnierKategorieValidationTest` mit Fake-Policy (Verkabelung + Beispielverletzungen)
|
||||
- [x] Konkrete Regeln/Limits gemäß ÖTO umsetzen (eigene Policy-Implementierung)
|
||||
- `OeToTurnierkategoriePolicy`: Harte Max-Limits umgesetzt (CSN: Höhe in cm; CDN: Klassen-Level). Sonderregeln (Pflichtbewerbe, Tageslimits, Genehmigungen) offen.
|
||||
- Tests: `OeToTurnierkategoriePolicyTest` (CSN/C und C-NEU; B vs. 140 cm; CDN/C und C-NEU; B vs. S)
|
||||
- [ ] Voraussetzung: Spezifikation von 📜 Rulebook Expert (A-5) abwarten (zur Ergänzung der Sonderregeln)
|
||||
- [x] **A-2** | Datenbankschema: Domänen-Hierarchie umgesetzt
|
||||
- [x] Tabellen `veranstaltungen`, `turniere`, `bewerbe`, `abteilungen` mit FK-Ketten
|
||||
- [x] `teilnehmer_konten` (Veranstaltungsebene), `turnier_kassa` (Turnierebene)
|
||||
- [x] Flyway-Migrationen V1–V3 (inkl. Turnier-Status `DRAFT`/`PUBLISHED`)
|
||||
- [x] `DomainHierarchyMigrationTest` grün
|
||||
|
||||
---
|
||||
|
||||
## 🟠 Sprint B — Kurzfristig (nächste Woche)
|
||||
## 🔴 Sprint A — Offen (höchste Priorität)
|
||||
|
||||
- [ ] **B-1** | CRUD-Endpunkte für alle Stammdaten-Entitäten (überarbeitet)
|
||||
- Multitenancy: Alle Endpunkte laufen im Tenant-Schema (Erkennung via `X-Event-Id` oder Subdomain; siehe A-1). IDs sind UUIDs. Fehlercodes: 400 (Bad Request), 404 (Not Found), 409 (Conflict), 422 (Validation), 423 (Locked – Tenant/Status).
|
||||
- Konventionen:
|
||||
- POST → 201 + `Location`-Header; GET (Liste) ist paginiert (`page`, `size`) + einfache Filter (`q`, spezifische Felder).
|
||||
- PUT = Voll-Update; PATCH = Teil-Update für Status/kleine Änderungen, wo sinnvoll.
|
||||
- Lösch-Strategie: Hard-Delete nur für Stammdaten ohne Referenzen; sonst 409 bei FK-Verletzung.
|
||||
- Standard-HTTP-Codes: `GET` 200, `POST` 201, `PUT` 200, `PATCH` 200, `DELETE` 204; Fehler gemäß obiger Liste.
|
||||
- [ ] **A-1** | Tenant-Isolation vollständig ausrollen ⚠️ BLOCKER
|
||||
- [x] ADR-0021 übernommen; `TenantWebFilter`, `TenantRegistry` (JDBC) implementiert
|
||||
- [x] Entries Service: `JdbcTenantRegistry`, `TenantMigrationsRunner`, MDC-Logging
|
||||
- [x] Flyway pro Tenant-Schema; Unit-Tests (`JdbcTenantRegistryTest`) grün
|
||||
- [ ] **Rollout auf weitere Services** (aktuell nur Entries Service migriert)
|
||||
- [ ] E2E-Isolationstest re-enablen (`@Disabled` wegen Jackson/Spring-Web-Autokonfiguration)
|
||||
|
||||
- Veranstaltung (Singleton pro Tenant)
|
||||
- [x] `GET /veranstaltung` — aktuelle Veranstaltung lesen
|
||||
- [x] `PUT /veranstaltung` — Veranstaltung aktualisieren
|
||||
- Hinweis: Erstellen/Löschen einer Veranstaltung erfolgt im Control-Plane (außerhalb des Tenant-Services); daher kein `POST/DELETE` hier.
|
||||
- [ ] **A-3** | Validierungs-Grundlage: Turnierkategorie-Limits
|
||||
- [x] Entkoppelte Policy-Schnittstelle + Bewerb-Descriptor implementiert
|
||||
- [x] Konkrete ÖTO-Regeln/Limits umgesetzt (eigene Policy-Implementierung)
|
||||
- [ ] Sonderregeln aus 📜 Rulebook B-2 Spezifikation einarbeiten (wartet auf Übergabe)
|
||||
|
||||
- Turniere
|
||||
- [x] `POST /turniere` — Turnier anlegen (Felder: `veranstaltungId` implizit aus Tenant, `oepsTurniernummer`, optional `bezeichnung`, `datumVon/Bis`, optional `status`—Default `DRAFT`)
|
||||
- [x] `GET /turniere` — Liste (Filter: `oepsTurniernummer`, Zeitraum, `status`; Paging)
|
||||
- [x] `GET /turniere/{id}` — Detail
|
||||
- [x] `PUT /turniere/{id}` — Voll-Update (ohne Status-Übergang)
|
||||
- Regeln: Bei `PUBLISHED` nur Metadaten änderbar, keine strukturellen Felder (z. B. `oepsTurniernummer`) → sonst `423 Locked`.
|
||||
- [x] `DELETE /turniere/{id}` — löschen (409, falls abhängige Bewerbe existieren; bei `PUBLISHED` grundsätzlich gesperrt → `423 Locked`)
|
||||
- Status-Management (neues Feld, Migration `V3__turniere_status.sql`): `DRAFT | PUBLISHED`
|
||||
- [x] `PATCH /turniere/{id}/status` — Statuswechsel mit Validierung
|
||||
- Erlaubt: `DRAFT → PUBLISHED` (setzt `publishedAt`-Timestamp serverseitig)
|
||||
- `PUBLISHED → DRAFT` nur erlaubt, wenn keine Nennungen/Zahlungen verbucht sind (sonst `409 Conflict`)
|
||||
- Unerlaubte Übergänge → `422 Validation` (inkl. Begründung im `problem+json`-Body)
|
||||
---
|
||||
|
||||
- Bewerbe (FK → Turnier)
|
||||
- [x] `POST /turniere/{turnierId}/bewerbe` — anlegen
|
||||
- [x] `GET /turniere/{turnierId}/bewerbe` — Liste im Turnier
|
||||
- [x] `GET /bewerbe/{id}` — Detail
|
||||
- [x] `PUT /bewerbe/{id}` — aktualisieren
|
||||
- [x] `DELETE /bewerbe/{id}` — löschen (409 bei existierenden Abteilungen/Nennungen; gesperrt falls zu `PUBLISHED` Turnier → `423 Locked`)
|
||||
## 🟠 Sprint B — Priorität 2 (diese Woche)
|
||||
|
||||
- Abteilungen (FK → Bewerb)
|
||||
- [x] `POST /bewerbe/{bewerbId}/abteilungen` — anlegen (Felder: `nr`, `bezeichnung`, `typ: SEPARATE_SIEGEREHRUNG | ORGANISATORISCH`)
|
||||
- [x] `GET /bewerbe/{bewerbId}/abteilungen` — Liste
|
||||
- [x] `GET /abteilungen/{id}` — Detail
|
||||
- [x] `PUT /abteilungen/{id}` — aktualisieren
|
||||
- [x] `DELETE /abteilungen/{id}` — löschen (gesperrt falls zu `PUBLISHED` Turnier → `423 Locked`)
|
||||
|
||||
- Hinweis: Filter `q` (LIKE/ILIKE) bei Bewerbe-Liste ist vorerst ausgelassen und kann nachgezogen werden.
|
||||
|
||||
- Reiter (Athleten-Stammdaten)
|
||||
- [ ] `POST/GET/GET{id}/PUT/DELETE /reiter` — Suche über `q` (Name, Lizenznr.), Filter: `lizenzKlasse`, `vereinId`
|
||||
|
||||
- Pferde (Pferde-Stammdaten)
|
||||
- [ ] `POST/GET/GET{id}/PUT/DELETE /pferde` — Suche `q` (Name, Lebensnr.), Filter: `jahrgang`, `besitzerId`
|
||||
|
||||
- Vereine
|
||||
- [ ] `POST/GET/GET{id}/PUT/DELETE /vereine` — Suche `q` (Name, Kürzel), Filter: `verband`
|
||||
|
||||
- Funktionäre
|
||||
- [ ] `POST/GET/GET{id}/PUT/DELETE /funktionaere` — Suche `q` (Name, Lizenznr.), Filter: `rolle`
|
||||
|
||||
- Technische Notizen
|
||||
- [ ] API-Doku per OpenAPI (Springdoc) veröffentlichen; Beispiel-Payloads für POST/PUT/PATCH (Statuswechsel)
|
||||
- [x] Konsistentes Error-Format (`problem+json`)
|
||||
- [ ] E2E-Tests: CRUD-Flows für Turnier → Bewerb → Abteilung inkl. FK-Constraints
|
||||
- [x] Migration `V3__turniere_status.sql` in Flyway integrieren und gegen H2/Postgres testen (Back/Forward kompatibel)
|
||||
- [x] Guardrails: Service-Ebene erzwingt Locks für `PUBLISHED` (PUT/DELETE) und valide Status-Transitions (PATCH)
|
||||
- [x] Problem+JSON-Details: `type`, `title`, `status`, `detail`, `instance` befüllen; bei `422` Begründung/Violations je Feld mitschicken.
|
||||
- [ ] **B-1** | CRUD-Endpunkte vervollständigen
|
||||
- [x] `Veranstaltung`: GET, PUT
|
||||
- [x] `Turniere`: POST, GET, GET{id}, PUT, DELETE, PATCH /status
|
||||
- [x] `Bewerbe`: POST, GET, GET{id}, PUT, DELETE
|
||||
- [x] `Abteilungen`: POST, GET, GET{id}, PUT, DELETE
|
||||
- [x] Konsistentes Error-Format (`problem+json`); Service-Guardrails für `PUBLISHED`-Lock
|
||||
- [ ] **`Reiter`**: POST/GET/GET{id}/PUT/DELETE (Suche `q`, Filter `lizenzKlasse`, `vereinId`)
|
||||
- [ ] **`Pferde`**: POST/GET/GET{id}/PUT/DELETE (Suche `q`, Filter `jahrgang`, `besitzerId`)
|
||||
- [ ] **`Vereine`**: POST/GET/GET{id}/PUT/DELETE (Suche `q`, Filter `verband`)
|
||||
- [ ] **`Funktionäre`**: POST/GET/GET{id}/PUT/DELETE (Suche `q`, Filter `rolle`)
|
||||
- [ ] OpenAPI-Dokumentation (Springdoc) veröffentlichen
|
||||
- [ ] E2E-Tests: CRUD-Flows Turnier → Bewerb → Abteilung inkl. FK-Constraints
|
||||
|
||||
- [ ] **B-2** | Kassa-Service implementieren
|
||||
- [ ] `TeilnehmerKonto`-Service: Saldo aus mehreren Turnieren aggregieren
|
||||
- [ ] `Zahlvorgang`-Service: Eine Zahlung auf Veranstaltungs-Ebene buchen
|
||||
- [ ] `Zahlvorgang`-Service: Zahlung auf Veranstaltungs-Ebene buchen
|
||||
- [ ] Rechnungs-Generierung: Separate Rechnung je Turnier aus einem Zahlvorgang
|
||||
- [ ] Endpunkte: `GET /veranstaltungen/{id}/kassa/saldo`, `POST /veranstaltungen/{id}/zahlvorgaenge`
|
||||
|
||||
- [ ] **B-3** | ÖTO-Validierung serverseitig absichern
|
||||
- [ ] Spezifikation von 📜 Rulebook Expert (Sprint A-5) umsetzen
|
||||
- [ ] OEPS-Nummern-Format validieren
|
||||
- [ ] FEI-ID-Format validieren
|
||||
- [ ] Spezifikation von 📜 Rulebook B-2 umsetzen (wartet auf Übergabe)
|
||||
- [ ] OEPS-Nummern-Format, FEI-ID-Format validieren
|
||||
- [ ] Lizenzklassen-Validierung (R1–R4, LZF)
|
||||
- [ ] Altersklassen-Kompatibilität Pferd × Bewerb validieren
|
||||
- [ ] Abteilungs-Zwangsteilung im CSN-C-NEU durchsetzen (Bewerb ≤95cm: ohne/mit Lizenz; ≥100cm: R1/R2+)
|
||||
|
||||
- [ ] **B-4** | Nennungs-Service (Grundstruktur)
|
||||
- [ ] Tabelle `nennungen` anlegen (FK → `abteilung_id`, Status: `NEU | GEPRÜFT | BESTÄTIGT | ABGELEHNT`)
|
||||
- [ ] `POST /turniere/{id}/nennungen` — Nennungs-Eingang vom Web-Formular
|
||||
- [ ] `GET /turniere/{id}/nennungen` — Postfach für Desktop-App (Meldestelle)
|
||||
- [ ] `PATCH /nennungen/{id}/status` — Bestätigen / Ablehnen
|
||||
- [ ] Altersklassen-Kompatibilität Pferd × Bewerb
|
||||
- [ ] Abteilungs-Zwangsteilung CSN-C-NEU (≤95cm: ohne/mit Lizenz; ≥100cm: R1/R2+)
|
||||
|
||||
---
|
||||
|
||||
## 🟡 Sprint C — Mittelfristig (in 2 Wochen)
|
||||
## 🟡 Sprint C — Priorität 3 (nächste Woche)
|
||||
|
||||
- [ ] **C-1** | Testdaten-Seeder implementieren
|
||||
- [ ] Reproduzierbare Veranstaltung mit 2 Turnieren (Neumarkt-Szenario)
|
||||
- [ ] Bewerbe mit korrekten Abteilungen (inkl. CSN-C-NEU Pflicht-Teilung)
|
||||
- [ ] Reiter, Pferde, Vereine als Stammdaten
|
||||
- [ ] Nennungen in verschiedenen Status-Stufen
|
||||
- [ ] Seeder via Gradle-Task ausführbar
|
||||
- [ ] **C-1** | Nennungs-Service (Grundstruktur)
|
||||
- [ ] Tabelle `nennungen` anlegen (FK → `abteilung_id`, Status-Automat)
|
||||
- [ ] `NennungsService`: Erstellen, Prüfen, Bestätigen, Ablehnen
|
||||
- [ ] Nennungs-Workflow-Endpunkte
|
||||
|
||||
- [ ] **C-2** | Statistik-Endpunkte
|
||||
- [ ] `GET /turniere/{id}/statistiken` — Statistiken pro Turnier
|
||||
- [ ] `GET /veranstaltungen/{id}/statistiken` — Aggregierte Statistiken über alle Turniere
|
||||
- [ ] **C-2** | Stammdaten-Seeder
|
||||
- [ ] Initiale Testdaten (Reiter, Pferde, Vereine) für Entwicklungsumgebung
|
||||
- [ ] Seed-Skript in `config/scripts/` ablegen
|
||||
|
||||
- [ ] **C-3** | LAN-Sync-Endpunkte (ADR-0022 ✅ freigegeben)
|
||||
- [ ] `SyncEvent`-Datenmodell in `core`-Modul definieren (KMP-shared, Phase 1)
|
||||
- [ ] SQLDelight-Tabellen `sync_events`, `sync_snapshots` anlegen
|
||||
- [ ] `LamportClock`-Implementierung (thread-safe, persistent)
|
||||
- [ ] WebSocket-Server auf Meldestelle-Desk (Ktor): HELLO/HELLO_ACK/SYNC_DELTA/SYNC_PUSH
|
||||
- [ ] mDNS-Discovery-Service integrieren (gemäß ADR-0020)
|
||||
- [ ] Domänen-Mastership-Validierung im Event-Handler
|
||||
- [ ] Reconnect-Logik mit Delta-Sync (`lastKnownSeq`)
|
||||
|
||||
---
|
||||
|
||||
## 📌 Abhängigkeiten
|
||||
|
||||
| Warte auf | Von wem |
|
||||
|------------------------------------------------|--------------------|
|
||||
| ADR-0021 (Tenant-Strategie) | 🏗️ Architect |
|
||||
| Validierungs-Spezifikation (OEPS, FEI, Lizenz) | 📜 Rulebook Expert |
|
||||
| Domänen-Modell final | 🏗️ Architect |
|
||||
| Warte auf | Von wem | Betrifft |
|
||||
|----------------------------------------|-------------|-----------------|
|
||||
| Rulebook B-2 Spezifikation | 📜 Rulebook | A-3, B-3 |
|
||||
| ~~ADR-0022 (LAN-Sync)~~ | ✅ Erledigt | C-3 freigegeben |
|
||||
| QA: E2E-Test-Umgebung Port-Binding Fix | 🧐 QA | A-1 @Disabled |
|
||||
|
||||
| Meine Aufgabe | Ermöglicht wem |
|
||||
|------------------------|--------------------------------|
|
||||
| CRUD-Endpunkte (B-1) | 🎨 Frontend: Backend-Anbindung |
|
||||
| Kassa-Service (B-2) | 🎨 Frontend: Kassa-Screen |
|
||||
| Nennungs-Service (B-4) | 🎨 Frontend: Nennungs-Postfach |
|
||||
---
|
||||
|
||||
## 💡 Empfehlungen (nach Priorität)
|
||||
|
||||
1. **A-1 Rollout** — Tenant-Isolation auf alle verbleibenden Services ausweiten; `@Disabled` E2E-Test re-enablen sobald
|
||||
Jackson-Fix vorliegt.
|
||||
2. **B-1 Reiter/Pferde/Vereine/Funktionäre** — Frontend wartet auf diese Endpunkte für ViewModel-Anbindung.
|
||||
3. **B-3 ÖTO-Validierung** — Erst nach Rulebook-Übergabe starten, aber Grundstruktur (Validator-Interface) schon
|
||||
vorbereiten.
|
||||
|
||||
Reference in New Issue
Block a user