docs: update roadmap, architecture, ADRs and README for phase 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
JannisandClaude Opus 5.5 committed 2026-10-09 18:25:11 +02:00
1 parent 3b36dce605
commit fe7067be93
7 files changed
+156 -35

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"PowerShell(aspire *)"
]
}
}
+15 -10
View File
@@ -5,21 +5,26 @@ Digitaler Familienplaner (PWA) auf .NET 10 + Aspire 13. Anforderungen: `docs/ARC
## Befehle
```bash
dotnet build FamilyPlanner.slnx # muss ohne Warnungen bauen (TreatWarningsAsErrors)
dotnet test FamilyPlanner.slnx # alle Tests; Integrationstests brauchen Docker
dotnet test tests/FamilyPlanner.UnitTests # schnell, ohne Docker
aspire run # bzw. dotnet run --project src/FamilyPlanner.AppHost
aspire publish -o artifacts/compose # Docker-Compose aus dem App-Modell erzeugen
dotnet build FamilyPlanner.slnx # muss ohne Warnungen bauen (TreatWarningsAsErrors)
dotnet test --solution FamilyPlanner.slnx # alle Tests (Microsoft.Testing.Platform); Integrationstests brauchen Docker
dotnet test --project tests/FamilyPlanner.UnitTests # schnell, ohne Docker
dotnet test --project tests/FamilyPlanner.Web.Tests # bUnit
dotnet test --project tests/FamilyPlanner.IntegrationTests # Aspire + SQL-Server-Container (~1–2 min)
aspire run # bzw. dotnet run --project src/FamilyPlanner.AppHost
aspire publish -o artifacts/compose # Docker-Compose aus dem App-Modell (Aspire-CLI >= 13.6)
# ohne passende CLI:
dotnet run --project src/FamilyPlanner.AppHost --no-launch-profile -- --operation publish --step publish --output-path ../../artifacts/compose
# Neue EF-Migration (aus dem Repo-Root)
# Neue EF-Migration (aus dem Repo-Root; Tool über dotnet-tools.json: `dotnet tool restore`)
dotnet ef migrations add <Name> -p src/FamilyPlanner.Infrastructure -s src/FamilyPlanner.MigrationService -o Persistence/Migrations
# Container-Images bauen
dotnet publish src/FamilyPlanner.Api -c Release /t:PublishContainer
dotnet publish src/FamilyPlanner.Web -c Release /t:PublishContainer
dotnet publish src/FamilyPlanner.MigrationService -c Release /t:PublishContainer
# Produktiv-Stack lokal bauen/starten
cd deploy && cp .env.example .env && docker compose up -d --build
```
Wichtig: Wenn der AppHost läuft, sind die DLLs von Api/Web gesperrt → vor `dotnet build`/Tests beenden.
Die Integrationstests starten einen eigenen, flüchtigen SQL-Container; der Dev-AppHost nutzt das Volume `familyplanner-sql-data`.
Lokaler Seed-Admin (AppHost-Parameter, siehe `src/FamilyPlanner.AppHost/appsettings.Development.json`): `admin` / `Admin123!ChangeMe` – Passwortwechsel beim ersten Login.
## Struktur
+92
View File
@@ -0,0 +1,92 @@
# Familienplaner
Digitaler Familienplaner als Progressive Web App: Einkaufsliste, Essenplan, Aufgaben & Ämtchen, Familienkalender
(inkl. Abo für iPhone/Outlook) und Chat – für Smartphone, Tablet und Desktop.
> **Status:** Phase 1 (Fundament) ist umgesetzt: Login, Rollen, Navigation, PWA-Grundgerüst, Aspire- und Docker-Betrieb.
> Die Fachfunktionen folgen phasenweise, siehe [`docs/ROADMAP.md`](docs/ROADMAP.md).
* Architektur: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
* Entscheidungen: [`docs/adr/`](docs/adr/)
## Voraussetzungen (Entwicklung)
* .NET SDK 10 (getestet mit 10.0.300)
* Docker Desktop bzw. Docker Engine (für SQL Server)
* optional: Aspire CLI (`aspire run`)
## Entwicklung
```bash
dotnet tool restore
aspire run # oder: dotnet run --project src/FamilyPlanner.AppHost
```
Das Aspire-Dashboard öffnet sich automatisch (Login-Link steht in der Konsole). Die App läuft unter
<https://localhost:7264>. Beim ersten Start wird der SQL-Server-Container mit persistentem Volume angelegt,
der MigrationService migriert die Datenbank und legt den Initial-Admin an:
| Benutzer | Passwort | Hinweis |
|---|---|---|
| `admin` | `Admin123!ChangeMe` | nur lokal (`src/FamilyPlanner.AppHost/appsettings.Development.json`), muss beim ersten Login geändert werden |
Das Passwort lässt sich per User Secrets überschreiben:
`dotnet user-secrets set "Parameters:seed-admin-password" "..." --project src/FamilyPlanner.AppHost`.
### Tests
```bash
dotnet build FamilyPlanner.slnx
dotnet test --solution FamilyPlanner.slnx # Integrationstests benötigen Docker
```
## Produktivbetrieb (Docker Compose)
```bash
cd deploy
cp .env.example .env # Passwörter und Domain anpassen
docker compose up -d --build # ohne Reverse Proxy, Web auf 127.0.0.1:8080
# oder mit automatischem HTTPS (Caddy + Let's Encrypt, Ports 80/443):
docker compose --profile proxy up -d --build
```
Die App benötigt **HTTPS** (sichere Cookies). Ohne Caddy funktioniert der Login nur über `http://localhost` oder hinter
einem eigenen Reverse Proxy mit TLS.
### Konfiguration (`deploy/.env`)
| Variable | Bedeutung |
|---|---|
| `SQL_SA_PASSWORD` | Passwort des SQL-Server-Administrators (komplex, min. 8 Zeichen) |
| `MSSQL_PID` | SQL-Server-Edition (`Express` = kostenlos) |
| `ADMIN_USERNAME`, `ADMIN_PASSWORD`, `ADMIN_EMAIL` | Initial-Admin, nur beim allerersten Start; Passwortwechsel wird erzwungen |
| `FAMILY_NAME` | Familienname (Vorbelegung der Familieneinstellungen beim ersten Start) |
| `WEB_BIND`, `WEB_PORT` | Host-Bindung des Web-Containers ohne Caddy |
| `DOMAIN` | Domain für Caddy/Let's Encrypt (Profil `proxy`) |
| `REGISTRY`, `TAG` | optionales Image-Präfix und Tag |
Weitere Einstellungen (alle Dienste) folgen der ASP.NET-Core-Konvention `Abschnitt__Schlüssel`, z. B.
`RateLimiting__LoginPermitLimit=10` (Api).
### Update
```bash
cd deploy
git pull
docker compose up -d --build # Migrationen laufen automatisch über den migrations-Container
```
### Backup & Restore
```bash
cd deploy
./scripts/backup.sh # schreibt backups/familyplanner-<datum>.bak
./scripts/restore.sh backups/familyplanner-20261009-120000.bak
```
Backups liegen zusätzlich im Docker-Volume `sql-backup` (14 Tage). Für eine regelmäßige Sicherung z. B. per Cron:
`0 3 * * * cd /opt/familyplanner/deploy && ./scripts/backup.sh`.
## Kalender-Abos (iPhone/Outlook)
Folgt in Phase 6 (ICS-Feed mit geheimem Abo-Link, siehe [ADR-0005](docs/adr/0005-ics-feed-token.md)).
+10 -4
View File
@@ -50,7 +50,9 @@ Siehe ADRs in [`docs/adr/`](adr/) für die Begründungen.
| `src/FamilyPlanner.Web` | Blazor-Host/BFF: statische Auslieferung der WASM-App + YARP-Proxy |
| `src/FamilyPlanner.Web.Client` | Blazor WebAssembly (MudBlazor), PWA |
| `tests/FamilyPlanner.UnitTests` | Domänenlogik, Rollen/Policies, Parser, ICS |
| `tests/FamilyPlanner.IntegrationTests` | End-to-End über `Aspire.Hosting.Testing` (echter SQL-Server-Container) |
| `tests/FamilyPlanner.IntegrationTests` | End-to-End über `Aspire.Hosting.Testing` (echter SQL-Server-Container, flüchtige DB) |
| `deploy/` | `docker-compose.yml`, `.env.example`, `Caddyfile`, Backup-/Restore-Skripte |
| `tools/` | Hilfsskripte (z. B. `make_icons.py` zum Erzeugen der PWA-Icons) |
| `tests/FamilyPlanner.Web.Tests` | bUnit-Komponententests |
### Vertical Slices in der Api
@@ -141,7 +143,9 @@ Ein Benutzer mit `MustChangePassword = true` erhält nur Zugriff auf `/api/auth/
## 7. Frontend
* Blazor Web App, **Interactive WebAssembly global, Prerendering aus** (ADR-0002). Der Web-Host rendert nur die HTML-Hülle.
* MudBlazor als Komponentenbibliothek (lokal ausgeliefert, kein CDN), deutsches UI, Texte vorbereitet für Lokalisierung (`IStringLocalizer`, `Resources/`).
* MudBlazor als Komponentenbibliothek (lokal ausgeliefert, kein CDN), deutsches UI. Lokalisierung vorbereitet: Kultur `de-DE`,
`AddLocalization()` registriert, alle Globalisierungsdaten geladen; Texte stehen derzeit direkt in den Komponenten und werden bei Bedarf in `Resources/*.resx` überführt.
* Ordner `Pages/` erfordert per `_Imports.razor` die Policy `PasswordChanged`; `Public/` enthält Login, Passwortwechsel und 404.
* Layout: MudLayout mit AppBar, Drawer (Desktop) bzw. Bottom-Navigation (Smartphone), Dark Mode (System-Default, umschaltbar, in `localStorage`).
* Eigener `AuthenticationStateProvider` (`ApiAuthenticationStateProvider`) holt `/api/auth/me` und liefert Claims inkl. Rollen.
* PWA: `manifest.webmanifest`, Icons 72–512 px + maskable, eigener Service Worker (App-Shell-Cache, Network-first für Navigation, keine Caches für `/api`/`/hubs`), Offline-Banner. Hintergrund-Sync für die Einkaufsliste über IndexedDB-Outbox ab Phase 3.
@@ -155,10 +159,12 @@ SignalR-Hubs in der Api, vom Web-Host per YARP (WebSockets) durchgereicht. Grupp
* **Lokal:** `aspire run` bzw. `dotnet run --project src/FamilyPlanner.AppHost`.
* **Produktion:** `deploy/docker-compose.yml` (SQL Server + Volume, migrations, api, web, optional Caddy für HTTPS).
Images werden über die SDK-Container-Unterstützung (`dotnet publish /t:PublishContainer`) bzw. Aspire-Publishing gebaut; alle Container laufen als Non-Root (`app`-User der Microsoft-Images).
Images werden mit dem gemeinsamen `Dockerfile` (Build-Argument `PROJECT`) gebaut; alle Container laufen als Non-Root (`app`-User der Microsoft-Images, SQL Server als `mssql`).
* Backups: `deploy/scripts/backup.sh` (SQL-Backup ins Volume `sql-backup` + Kopie nach `deploy/backups/`), `deploy/scripts/restore.sh`.
* `aspire publish` mit `AddDockerComposeEnvironment("compose")` erzeugt alternativ eine Compose-Datei aus dem App-Modell.
* Konfiguration ausschließlich per Umgebungsvariablen/`.env` (siehe `deploy/.env.example`), lokal User Secrets / Aspire-Parameter.
* Health Checks: `/alive` (Liveness), `/health` (Readiness inkl. DB) – in Produktion nur vom Docker-Netz bzw. per Health-Check-Befehl erreichbar, der Web-Host proxyt sie nicht.
* Health Checks: `/alive` (Liveness), `/health` (Readiness inkl. DB bei der Api) – auch in Produktion gemappt (nur Status-Text, keine Details).
Der Web-Host beantwortet `/health` selbst und leitet es nicht an die Api weiter.
* Data Protection Keys werden in der Datenbank gespeichert (Api), damit Cookies Container-Neustarts überleben.
## 10. Querschnitt
+18 -15
View File
@@ -3,20 +3,21 @@
Legende: `[x]` erledigt, `[ ]` offen. Nach jeder Phase: Build ohne Warnungen, Tests grün, Start über Aspire, Commit.
## Phase 1 – Fundament
- [ ] Solution (`FamilyPlanner.slnx`), alle Projekte laut Architektur inkl. MigrationService
- [ ] `Directory.Build.props` (Nullable, TreatWarningsAsErrors), `Directory.Packages.props`, `.editorconfig`, `global.json`, `.gitignore`
- [ ] Aspire AppHost: SQL Server mit persistentem Volume, Datenbank-Ressource, MigrationService, Api, Web (`WithReference`, `WaitFor`, `WaitForCompletion`)
- [ ] ServiceDefaults: OpenTelemetry, `/health`, `/alive`, Resilience, Service Discovery
- [ ] EF Core `AppDbContext` mit Identity, `AuditableEntity`, Soft Delete, Audit-Interceptor, Initial-Migration
- [ ] Data Protection Keys in der Datenbank
- [ ] Seeding: Rollen Admin/Familie/Kind/Gast, Initial-Admin aus Konfiguration mit erzwungener Passwortänderung, Familieneinstellungen
- [ ] Auth-API: Login (Benutzername/E-Mail, Angemeldet bleiben), Logout, Me, Passwort ändern; Lockout; Rate Limiting
- [ ] Web-Host als BFF mit YARP (`/api`, `/hubs`, `/ics`)
- [ ] Blazor-Shell mit MudBlazor: Login, Passwortwechsel, Navigation, Platzhalterseiten, Dark Mode, deutsche Texte
- [ ] PWA-Grundgerüst: Manifest, Icons, Service Worker, Offline-Hinweis
- [ ] Docker: Container-Images (non-root), `deploy/docker-compose.yml`, `deploy/.env.example`, Aspire-Docker-Compose-Publishing
- [ ] Tests: Unit (Rollen/Domain), Integration (Login-Flow über Aspire.Hosting.Testing), bUnit (Login-Seite)
- [ ] CI-Pipeline (GitHub Actions)
- [x] Solution (`FamilyPlanner.slnx`), alle Projekte laut Architektur inkl. MigrationService
- [x] `Directory.Build.props` (Nullable, TreatWarningsAsErrors), `Directory.Packages.props`, `.editorconfig`, `global.json`, `.gitignore`
- [x] Aspire AppHost: SQL Server mit persistentem Volume, Datenbank-Ressource, MigrationService, Api, Web (`WithReference`, `WaitFor`, `WaitForCompletion`)
- [x] ServiceDefaults: OpenTelemetry, `/health`, `/alive`, Resilience, Service Discovery
- [x] EF Core `AppDbContext` mit Identity, `AuditableEntity`, Soft Delete, Audit-Interceptor, Initial-Migration
- [x] Data Protection Keys in der Datenbank
- [x] Seeding: Rollen Admin/Familie/Kind/Gast, Initial-Admin aus Konfiguration mit erzwungener Passwortänderung, Familieneinstellungen
- [x] Auth-API: Login (Benutzername/E-Mail, Angemeldet bleiben), Logout, Me, Passwort ändern; Lockout; Rate Limiting; CSRF-Header
- [x] Web-Host als BFF mit YARP (`/api`, `/hubs`, `/ics`)
- [x] Blazor-Shell mit MudBlazor: Login, Passwortwechsel, Navigation (Drawer + Bottom-Navigation), Platzhalterseiten, Dark Mode, deutsche Texte
- [x] PWA-Grundgerüst: Manifest, Icons, Service Worker, Offline-Hinweis
- [x] Docker: Container-Images (non-root), `deploy/docker-compose.yml`, `deploy/.env.example`, Aspire-Docker-Compose-Publishing
- [x] Backup-/Restore-Skripte (erste Version), README (erste Version)
- [x] Tests: Unit (Rollen/Policies/Domain/Interceptor), Integration (Login-Flow über Aspire.Hosting.Testing), bUnit (Login-Seite)
- [x] CI-Pipeline (GitHub Actions)
## Phase 2 – Rollen & Admin
- [ ] Policies vollständig (siehe ARCHITECTURE §4) inkl. Unit-Tests je Policy
@@ -79,7 +80,9 @@ Legende: `[x]` erledigt, `[ ]` offen. Nach jeder Phase: Build ohne Warnungen, Te
- [ ] Rechte Kind/Gast
## Phase 9 – Härtung
- [ ] Security-Header (CSP, HSTS, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
- [ ] Content-Security-Policy (Basis-Header X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, HSTS sind seit Phase 1 aktiv)
- [ ] Eigener SQL-Login mit minimalen Rechten statt `sa` für Api/MigrationService
- [ ] Service-Worker-Cache-Version automatisch beim Build setzen
- [ ] Rate Limiting vervollständigen
- [ ] Tests vervollständigen (Policies, ICS, Wiederholungen, Integration)
- [ ] Barrierefreiheit (WCAG 2.1 AA), Performance (AOT/Trimming prüfen, Lazy Loading)
+2
View File
@@ -14,6 +14,8 @@ wenn Browser-App und Api dieselbe Origin haben.
* Die **Api besitzt Identity** und stellt das Cookie `__Host-fp.auth` aus (HttpOnly, Secure, SameSite=Strict). Durch den Proxy ist es aus Browsersicht ein First-Party-Cookie.
* Rendermodus: **InteractiveWebAssembly global mit `prerender: false`**. Der Web-Host kennt dadurch keinen Auth-Zustand und braucht weder Identity noch geteilte Data-Protection-Keys.
* Der WASM-Client nutzt einen eigenen `AuthenticationStateProvider`, der `/api/auth/me` abfragt.
* Die Seiten tragen `[Authorize]`-Metadaten für den Client-Router. Damit der Host diese nicht selbst auswertet (er hat kein Auth-Schema),
registriert er nur die Policy-Namen und liefert die Razor-Endpunkte mit `.AllowAnonymous()` aus. Die Hülle enthält keine Daten.
* CSRF-Schutz: SameSite=Strict plus Pflicht-Header `X-Requested-With` für alle schreibenden API-Requests (ADR-0006).
## Alternativen
+12 -6
View File
@@ -4,15 +4,21 @@
* Datum: 2026-10-09
## Entscheidung
* .NET 10 (SDK 10.0.300, `global.json` mit `rollForward: latestFeature`), Aspire 13.6.1 (`Aspire.AppHost.Sdk`).
* .NET 10 (`global.json`: mindestens 10.0.100, `rollForward: latestFeature`; entwickelt mit SDK 10.0.300), Aspire 13.6.1 (`Aspire.AppHost.Sdk`).
* Zentrales Paketmanagement über `Directory.Packages.props`; nur stabile Versionen (Stand 2026-10: EF Core/ASP.NET 10.0.12, MudBlazor 9.11.0,
YARP 2.3.0, OpenTelemetry 1.19, FluentValidation 12.1.1, Ical.Net 5.2.3, xunit.v3 4.0.1, bUnit 2.11.3).
YARP 2.3.0, Service Discovery/Resilience 10.10.0, OpenTelemetry 1.19, FluentValidation 12.1.1, Ical.Net 5.2.3, xunit.v3 4.0.1, bUnit 2.11.3).
`Aspire.Hosting.AppHost` wird implizit vom AppHost-SDK referenziert und steht deshalb nicht in `Directory.Packages.props`.
* Solution im XML-Format **`FamilyPlanner.slnx`** (Default des .NET-10-SDK, von Visual Studio, Rider und `dotnet` unterstützt) statt `.sln`.
* `Directory.Build.props`: Nullable, ImplicitUsings, `TreatWarningsAsErrors`.
* Tests: xUnit v3 (VSTest-Adapter, damit `dotnet test` ohne Zusatzkonfiguration läuft), bUnit für Komponenten,
`Aspire.Hosting.Testing` für Integrationstests mit echtem SQL-Server-Container.
* Container-Images über die SDK-Container-Unterstützung (`dotnet publish /t:PublishContainer`), Basis `mcr.microsoft.com/dotnet/aspnet:10.0`,
Benutzer `app` (non-root, Standard der .NET-8+-Images). Keine handgeschriebenen Dockerfiles nötig.
* Tests: xUnit v3 auf **Microsoft.Testing.Platform** (`global.json` → `"test": { "runner": "Microsoft.Testing.Platform" }`).
xunit.v3 4.x unterstützt das alte VSTest-Ziel mit dem .NET-10-SDK nicht mehr; daher entfallen `Microsoft.NET.Test.Sdk` und `coverlet.collector`.
Aufruf: `dotnet test --solution FamilyPlanner.slnx` bzw. `dotnet test --project <Testprojekt>`.
bUnit für Komponenten, `Aspire.Hosting.Testing` für Integrationstests mit echtem SQL-Server-Container (flüchtige DB über `FamilyPlanner:EphemeralDatabase=true`).
* Der AppHost nutzt DCP und Dashboard aus den NuGet-Paketen (`AspireUseCliBundle=false`, Warnung ASPIRE010 unterdrückt), damit `dotnet run`
und die Integrationstests auch ohne installierte Aspire-CLI laufen (CI).
* Container-Images: ein gemeinsames, mehrstufiges `Dockerfile` (Build-Argument `PROJECT`), Basis `mcr.microsoft.com/dotnet/aspnet:10.0`,
Benutzer `app` (non-root). Damit kann der Linux-Host die Images ohne installiertes .NET-SDK direkt per `docker compose build` bauen.
`curl` ist für die Docker-Health-Checks enthalten.
## Konsequenzen
Werkzeuge, die nur `.sln` lesen, können über `dotnet sln` eine `.sln` erzeugen; die CI und lokale Entwicklung nutzen `.slnx` direkt.