Files
svc-essensplanung/Doku/plan.md
T
jens 1589088379 M1: CRUD-Slice 'Mahlzeit' Schritt 1 – Logik + Daten + Tests
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).
2026-09-19 08:55:54 +02:00

87 lines
4.4 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.
# Implementierungsplan
Status: **final** (2026-09-13, abgestimmt) · Basis: [`anforderungen.md`](anforderungen.md), [`architektur.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 Client
`essens`), Auto-Provision des lokalen Users (wie `svc-webui`). HA bezieht die
Tagesansicht über `GET /api/today` mit **statischem API-Token**
(`X-Api-Token` aus `.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.md` leergelegt (`src/UI`, `src/Logic`, `src/Data`)
- Keycloak-OIDC: manueller `OidcClient` (kein `league/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`
(Volume `data/` für SQLite, `.env`), Network `infra-traefik_traefik-net` (external),
Traefik-Labels → `essen.ljh31.de` + ACME + redirect-to-https
- `phpunit.xml`, `tests/`, htmx + SortableJS in `public/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`
(Tabelle `essens_mahlzeiten`, wochentage als CSV), `MahlzeitMapper/Provider/Processor`, Migration.
Tests: UseCase/Model-Unit + Provider/Processor-Integration (in-mem SQLite).
**DoD 1:** `composer test` grü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` (Status `aktiv`/`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 → `CreateDish` mit Referenz
- **DoD:** Reorder bleibt nach Reload bestehen; Inspiration klickbar adoptierend
### M4 – Härtung & API
- `X-Api-Token`-Guard (optional, `.env`) für `GET /api/today` (HA-ready)
- Fehlerseiten, Logging, Backup-Hinweis (SQLite-Datei `data/`)
- **DoD:** `curl -H "X-Api-Token: …" /api/today` liefert Tagesgericht-JSON;
DoD-Punkte aus `anforderungen.md` abgehakt
### 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 `essens` in realm `family` anlegen (Redirect-URI `https://essen.ljh31.de`)
- **DoD:** Gesamt-DoD aus `anforderungen.md` erfü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 test` bzw. `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 |