Rich-Model Mahlzeit (MahlzeitStatus, Wochentag) mit Invarianten und Zustandsübergängen; DTOs + MahlzeitFactory (reine Mapping-Funktionen); Manager (create hängt an Ende an: maxSortOrder+1, find → 404) und Create/Update/Delete-UseCases mit Transaktion + Events nach Commit. Data-Layer: MahlzeitEntity (Tabelle essens_mahlzeiten, wochentage als CSV), Mapper (CSV/Enum/null), Provider (find/list/maxSortOrder), Processor (create/save/delete). DI-Bindings in services.yaml. ClockInterface (Logic) + SystemClock (Data). Tests: Model/Factory/UseCase-Unit + Provider/Processor/Mapper-Integration (In-Mem-SQLite). composer test grün (53 Tests, 161 Assertions).
4.4 KiB
4.4 KiB
Implementierungsplan
Status: final (2026-09-13, abgestimmt) · Basis: anforderungen.md, architektur.md, ADR 001–007
Prinzipien
- Vertikal geschnitten: Jedes Milestone ist ein vollständiger UI→Logic→Data-Slice und am Ende lauffähig/testbar (kein Layer-Reihenbau).
- Ein Milestone = ein Review-Pausenpunkt. Jeder MS wird einzeln abgenommen, bevor der nächste startet.
- Kein CI – PHPUnit läuft manuell (
composer test). - Auth: Web-UI über Keycloak-OIDC (realm
family, eigener Clientessens), Auto-Provision des lokalen Users (wiesvc-webui). HA bezieht die Tagesansicht überGET /api/todaymit statischem API-Token (X-Api-Tokenaus.env, kein Keycloak-Refresh für HA). Damit wird ADR-003 in M0 entsprechend überarbeitet. - Deploy (M5) erst nach expliziter Freigabe.
Milestones
M0 – Skelett + Auth-Grundgerüst
- Symfony 7.4 LTS, PHP 8.4, FrankenPHP:
composer.json,config/(framework, doctrine/SQLite, twig, routing),public/index.php - Struktur laut
architektur.mdleergelegt (src/UI,src/Logic,src/Data) - Keycloak-OIDC: manueller
OidcClient(keinleague/oauth2-client, kein Security-Bundle; vgl. ADR-003), Login/Callback/Logout im UI-Layer,sub/email-Claim → lokale User-Zeile (Auto-Provision) - Docker:
Dockerfile(php:8.4-frankenphp),compose.yaml(Volumedata/für SQLite,.env), Networkinfra-traefik_traefik-net(external), Traefik-Labels →essen.ljh31.de+ ACME + redirect-to-https phpunit.xml,tests/, htmx + SortableJS inpublic/js/- ADR-003 überarbeitet (Keycloak statt „kein Login")
- DoD: Container läuft,
GET /→ Keycloak-Login, danach Hello-World-Seite
M1 – CRUD-Slice (Model: Mahlzeit – Namenskonszept: Domäne DE, Tech-EN, s. architektur.md §0.1)
- Schritt 1 (Logik+Daten):
src/Logic/Essen–Mahlzeit-Model (+MahlzeitStatus,Wochentag),Mahlzeit{Create,Update}Request/Response-DTOs,MahlzeitFactory,Mahlzeit{Provider,Processor}Interface,MahlzeitManager,Mahlzeit{Create,Update,Delete}UseCase,MahlzeitListQuery,MahlzeitNotFoundException(→404), Events;src/Data/Essen–MahlzeitEntity(Tabelleessens_mahlzeiten, wochentage als CSV),MahlzeitMapper/Provider/Processor, Migration. Tests: UseCase/Model-Unit + Provider/Processor-Integration (in-mem SQLite). DoD 1:composer testgrün, CRUD vollständig über Logic-API testbar. - Schritt 2 (UI):
MahlzeitController+templates/mahlzeiten/+ Routen/mahlzeiten[/{id}](POST/Redirect/Flash, Wochentage als Checkboxes) + Controller-Feature-Test (eingeloggte Session). - DoD: Gericht anlegen/bearbeiten/löschen im Browser (als eingeloggte/r Nutzer)
M2 – Status & Wochentage
CheckOffDish/UncheckDish(Statusaktiv/gecheckt),ArchiveDish- Wochentag-Tags (Mo–So) setzen/ändern, Liste nach Tag gruppier-/filterbar
- DoD: Check-Off via htmx-Toggle, Tagesansicht, Archiv-Tab
M3 – Reorder + Inspiration
ReorderDishes(SortableJS-change→ Position-Ints, persistiert)SuggestDishes-Query: Slow 0.5 / Rarität 0.3 / Hotness 0.2 (ADR-006), 5 Vorschläge + „Weitere 5", 1-Klick-Adopt →CreateDishmit Referenz- DoD: Reorder bleibt nach Reload bestehen; Inspiration klickbar adoptierend
M4 – Härtung & API
X-Api-Token-Guard (optional,.env) fürGET /api/today(HA-ready)- Fehlerseiten, Logging, Backup-Hinweis (SQLite-Datei
data/) - DoD:
curl -H "X-Api-Token: …" /api/todayliefert Tagesgericht-JSON; DoD-Punkte ausanforderungen.mdabgehakt
M5 – Deploy (erst nach Freigabe)
- Stack auf
docker1:/root/stacks/svc-essensplanung,git pull && docker compose up -d - Traefik:
essen.ljh31.de, Let's-Encrypt - Keycloak: Client
essensin realmfamilyanlegen (Redirect-URIhttps://essen.ljh31.de) - DoD: Gesamt-DoD aus
anforderungen.mderfüllt, erreichbar ohne LAN-IP
Test-Strategie (quer)
- PHPUnit je Milestone: UseCase-Unit (in-mem SQLite, Logic-Dependencies als Fakes) + Feature-Tests (HTTP gegen Test-DB)
- Manuell:
docker compose run app composer testbzw.vendor/bin/phpunit - Kein CI (bewusst, s. Prinzipien)
Abnahme-Historie
| MS | Datum | Status |
|---|---|---|
| M0 | offen | |
| M1 | offen | |
| M2 | offen | |
| M3 | offen | |
| M4 | offen | |
| M5 | offen |