Adds SlpModularCms.Api.SlpSoftware and extracts shared CmsHost composition
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Successful in 6m10s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m59s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m27s
Continuous Integration / backend-test (pull_request) Failing after 7m48s
Continuous Integration / frontend-build (pull_request) Successful in 2m5s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / config (pull_request) Successful in 11s
Continuous Integration / changes (pull_request) Successful in 21s
Continuous Integration / backend-build (pull_request) Successful in 6m10s
Continuous Integration / vulnerability-scan (pull_request) Successful in 4m59s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m27s
Continuous Integration / backend-test (pull_request) Failing after 7m48s
Continuous Integration / frontend-build (pull_request) Successful in 2m5s
Continuous Integration / frontend-test (pull_request) Successful in 4m24s
Continuous Integration / frontend-lint (pull_request) Successful in 2m0s
Continuous Integration / publish-test (pull_request) Skipped
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-test (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project in the Clients solution folder, intended to eventually become the deployed API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as SlpModularCms.Api plus a future Offerings module. - Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), shared by both Client projects so they cannot drift apart - Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into Core, since CmsHost cannot live in Api but Core cannot depend on Api - Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database and dev ports (5286/7223, distinct from Api's and Api.Slave's) - Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline regression tests (security headers, health check, SPA fallback, rate limiting), scoped to Api per NFR Design - Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave - Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing refresh token) as unhandled errors -- pre-existing, unrelated to this feature's own scope, found while testing the new instance Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# Application Design Plan — SlpSoftware Production API
|
||||
|
||||
Dit plan beschrijft hoe de high-level applicatie-ontwerp-artefacten voor deze feature worden opgesteld. Beantwoord eerst de vragen hieronder; na jouw goedkeuring wordt het plan uitgevoerd.
|
||||
|
||||
Context die ik al heb geverifieerd in de code (niet aangenomen):
|
||||
- `ModuleOrchestrator` (`SlpModularCms.Core/Hosting/ModuleOrchestrator.cs`) ontdekt modules **dynamisch** door `.dll`-bestanden op schijf te scannen — er is nergens een expliciete "welke modules host ik"-lijst in `Program.cs`. Dat betekent: welke modules een Client-project host, wordt volledig bepaald door welke Module-projecten dat `.csproj` referenceert, niet door code in `Program.cs` zelf.
|
||||
- `SlpModularCms.Api/Program.cs` bevat, op de bootstrap-regels na (`WebApplication.CreateBuilder`, `appsettings.local.json`), **geen enkele project-specifieke branch** — alles is generiek/config-gedreven. Dat maakt een verregaande extractie (FR-3) haalbaar.
|
||||
- `SlpModularCms.Modules.Master` volgt het patroon: `Controllers/` → `Services/` (`I{X}Service`/`{X}Service`) → `Repositories/` (`I{X}Repository`/`{X}Repository`) → `Data/{X}DbContext.cs`, plus `Models/` voor DTO's/requests en `{Module}Module.cs` (`IModule`-implementatie).
|
||||
|
||||
## Uitvoeringschecklist
|
||||
|
||||
- [x] Stap A — `components.md`: componenten identificeren (CmsHost-extractie, Offerings-module met sub-componenten) met verantwoordelijkheden
|
||||
- [x] Stap B — `component-methods.md`: methode-signaturen per component (geen gedetailleerde business rules — dat komt in Functional Design)
|
||||
- [x] Stap C — `services.md`: servicedefinities en orkestratiepatronen (o.a. featured-exclusiviteit, reorder-logica uit de user stories)
|
||||
- [x] Stap D — `component-dependency.md`: afhankelijkheidsmatrix + datastroom (Core ↔ Api ↔ Api.SlpSoftware ↔ Offerings)
|
||||
- [x] Stap E — `application-design.md`: consolidatie van bovenstaande in één document
|
||||
- [x] Stap F — Consistentiecontrole: komt het ontwerp overeen met requirements.md (FR-1..FR-9) en stories.md (US-01..US-12)?
|
||||
|
||||
---
|
||||
|
||||
## Vragen
|
||||
|
||||
### Vraag 1 — Vorm van de `CmsHost`-extractie (FR-3)
|
||||
`Api/Program.cs` bevat, buiten de bootstrap-regels, geen project-specifieke logica. Dat maakt twee uitersten mogelijk voor de extractie.
|
||||
|
||||
Hoe ver moet de extractie naar `SlpModularCms.Core` gaan?
|
||||
|
||||
A) **Eén volledig entrypoint** — `CmsHost.RunAsync(string[] args)` bevat de hele samenstelling (services + pipeline + `app.Run()`); beide `Program.cs`-bestanden worden dan letterlijk een paar regels (`return CmsHost.RunAsync(args);` + evt. bootstrap-overrides). Minimaliseert duplicatie/drift maximaal, maar geeft een individueel project weinig ruimte om ooit af te wijken zonder de gedeelde methode te wijzigen.
|
||||
B) **Twee gedeelde methodes** — `CmsHost.ConfigureServices(WebApplicationBuilder)` en `CmsHost.ConfigurePipeline(WebApplication)`, die elk project vanuit zijn eigen dunne `Program.cs` aanroept (zoals vandaag al met `AddCoreInfrastructure` etc. gebeurt, maar dan als één samengestelde aanroep per fase). Iets meer code per project, maar elk project behoudt een zichtbaar `Program.cs` waarin het makkelijk is om ooit één stap toe te voegen/over te slaan zonder `Core` te wijzigen.
|
||||
X) Anders (beschrijf hieronder na de [Answer]:-tag)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
### Vraag 2 — Architectuurpatroon voor de Offerings-module
|
||||
`Modules.Master` gebruikt een Repository+Service-laag (`ICmsInstanceRepository` → `ICmsInstanceService` → Controller). Bij Requirements Analysis heb je de Property-Based Testing-extensie overgeslagen met als reden dat dit een eenvoudige CRUD-achtige module is zonder significante bedrijfslogica.
|
||||
|
||||
Moet de Offerings-module hetzelfde Repository+Service-patroon volgen (consistent met Master), of is dat voor deze module onnodige indirectie?
|
||||
|
||||
A) Repository+Service (consistent met Master) — `IOfferingRepository` + `IOfferingsService`, ook al is de module zelf simpel
|
||||
B) Alleen Service, geen Repository — `IOfferingsService` praat direct met `OfferingsDbContext` (minder indirectie voor een module die je zelf als eenvoudige CRUD hebt gekarakteriseerd)
|
||||
X) Anders (beschrijf hieronder na de [Answer]:-tag)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Vraag 3 — Controllersplitsing publiek vs. admin
|
||||
Requirements.md scheidt al duidelijk het publieke `GET /api/v1/offerings` (FR-6, anoniem) van de admin-CRUD (FR-7, `AdminOnly`).
|
||||
|
||||
Moet dit ook twee aparte controllers worden, of één controller met gemengde autorisatie per actie?
|
||||
|
||||
A) Twee controllers — `OfferingsController` (publiek, alleen `GET`) en `OfferingsAdminController` (CRUD + reorder, `AdminOnly`) — duidelijke scheiding, moeilijker om per ongeluk een admin-actie anoniem te laten
|
||||
B) Eén controller — `OfferingsController` met `[AllowAnonymous]` op de publieke `GET` en `[Authorize(Policy = "AdminOnly")]` op de rest
|
||||
X) Anders (beschrijf hieronder na de [Answer]:-tag)
|
||||
|
||||
[Answer]: B
|
||||
|
||||
### Vraag 4 — Id-strategie voor nieuw aangemaakte offerings
|
||||
De drie bestaande referentiewaarden (FR-8) gebruiken leesbare slugs (`pakket_01`, `pakket_02`, `pakket_03`). Het publieke contract (FR-6) verwacht een `string`-veld `id`, dus zowel een GUID als een handmatige slug is technisch mogelijk.
|
||||
|
||||
Hoe moet de `Id` van een **nieuw** aangemaakte offering tot stand komen?
|
||||
|
||||
A) Automatisch gegenereerd (GUID as string) — simpel, geen validatie op uniekheid/formaat nodig, consistent met andere entiteiten in dit systeem (bijv. `CmsInstance.Id`)
|
||||
B) Door de CMS Administrator zelf opgegeven als leesbare slug — consistent met de bestaande `pakket_XX`-stijl, vereist wel validatie (uniek, toegestane tekens)
|
||||
X) Anders (beschrijf hieronder na de [Answer]:-tag)
|
||||
|
||||
[Answer]: A
|
||||
|
||||
### Vraag 5 — Verwijdersemantiek
|
||||
Requirements.md signaleert een nog open punt bij SECURITY-13 (audit-trail op content-mutaties). US-06/US-07 beschrijven "verwijderen" zonder te specificeren of dat een echte database-delete is of een soft-delete.
|
||||
|
||||
Hoe moet "een offering verwijderen" op databaseniveau werken?
|
||||
|
||||
A) Hard delete — de rij wordt echt verwijderd uit `OfferingsDbContext`; simpelst, maar draagt niet bij aan het SECURITY-13-openpunt
|
||||
B) Soft delete — een `IsDeleted`/`DeletedAt`-veld, verwijderde offerings worden uit alle queries gefilterd maar blijven in de database staan; simpele, gedeeltelijke invulling van het SECURITY-13-openpunt (geen volledige audit trail, maar wel behoud van de laatste staat vóór verwijdering)
|
||||
X) Anders (beschrijf hieronder na de [Answer]:-tag)
|
||||
|
||||
[Answer]: B
|
||||
Reference in New Issue
Block a user