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
5.6 KiB
Components — SlpSoftware Production API
1. CmsHost (new — SlpModularCms.Core.Hosting)
Purpose: Shared hosting-composition logic extracted from SlpModularCms.Api/Program.cs (FR-3), consumed by both SlpModularCms.Api and SlpModularCms.Api.SlpSoftware.
Responsibilities:
- Compose the standard set of service registrations every Client project needs (logging, Sentry, module discovery, core infrastructure, CORS, rate limiting, health checks, security headers, observability, Data Protection, controllers).
- Compose the standard HTTP pipeline every Client project needs (migrations, exception handling, security headers, rate limiting, OpenAPI/Scalar in Development, HTTPS redirection, static content, CORS, module middleware, authentication/authorization, controllers, health checks, Sentry tunnel, SPA fallbacks).
- Not responsible for: bootstrapping the
WebApplicationBuilderitself, or loadingappsettings.local.json— those two lines stay in each project's ownProgram.cs(decision Q1 = B: two granular methods, not one entrypoint that owns everything).
Interfaces: ConfigureServices(WebApplicationBuilder), ConfigurePipeline(WebApplication, ModuleOrchestrator) — see component-methods.md.
2. SlpModularCms.Api.SlpSoftware (new Client project)
Purpose: The new deployable Client (FR-1), first project in the Clients solution folder. Eventually the production host for test.slpsoftware.nl / slpsoftware.nl (Operations phase, D-15).
Responsibilities:
- Reference
SlpModularCms.Core,SlpModularCms.Modules.Identity,SlpModularCms.Modules.Availability,SlpModularCms.Modules.Master, and the newSlpModularCms.Modules.Offerings(FR-2) — module composition is driven entirely by which Module projects are referenced (ModuleOrchestratordiscovers modules from.dllfiles on disk, not from any list inProgram.cs). - Thin
Program.cs: bootstrap the builder, loadappsettings.local.json, callCmsHost.ConfigureServices/ConfigurePipeline. - Own its own
appsettings.json/appsettings.Development.json/appsettings.local.json(per thedotnet-appsettingsskill pattern already used byApi).
Interfaces: None beyond Program.cs itself — it's a composition root, not a library.
3. Offering (new entity — SlpModularCms.Modules.Offerings.Data.Entities)
Purpose: The persisted record behind both the public read contract (FR-6) and admin management (FR-7).
Responsibilities: Hold Id (Guid, per Q4 = A), Title, Description, Price, PriceNote, Features (ordered list), CtaLabel, Featured, DisplayOrder, IsDeleted, DeletedAt (per Q5 = B, soft delete).
4. OfferingsDbContext (new — SlpModularCms.Modules.Offerings.Data)
Purpose: Module-isolated EF Core context for the Offering entity, following the existing per-module DbContext pattern (MasterDbContext, AvailabilityDbContext) — MariaDB via UseMySQL, its own migrations assembly.
Responsibilities: DbSet<Offering> Offerings; model configuration (max lengths for Title/Description/Price/PriceNote/CtaLabel per SECURITY-05).
5. IOfferingRepository / OfferingRepository (new — SlpModularCms.Modules.Offerings.Repositories)
Purpose: Data-access layer between the service and OfferingsDbContext, mirroring Modules.Master's ICmsInstanceRepository/CmsInstanceRepository (decision Q2 = A: keep the Repository+Service pattern consistent across modules, even though this module is individually simple).
Responsibilities: CRUD against Offering rows, always excluding soft-deleted rows except where the service explicitly needs them; ordering by DisplayOrder.
6. IOfferingsService / OfferingsService (new — SlpModularCms.Modules.Offerings.Services)
Purpose: Business orchestration layer — the one component that knows the rules from stories.md that a repository alone shouldn't own.
Responsibilities:
- Enforce the "at most one featured offering" exclusivity rule (US-10) when creating/updating.
- Own the
DisplayOrdersemantics for both reorder interactions: full-list reorder from drag-and-drop (US-08) and adjacent swap from the up/down buttons (US-09). - Assign
Id(newGuid) and initialDisplayOrder(append to end) on creation. - Apply soft-delete (US-06/US-07 — deleting the last remaining offering is always allowed, decision Q4 in requirements.md).
- Map between
Offeringentities and the DTOs used by the controller.
7. OfferingsController (new — SlpModularCms.Modules.Offerings.Controllers)
Purpose: Single HTTP-facing component for the module (decision Q3 = B: one controller, mixed authorization per action, rather than a public/admin split into two controllers).
Responsibilities: Expose GET /api/v1/offerings ([AllowAnonymous], FR-6) and the admin CRUD + reorder actions ([Authorize(Policy = "AdminOnly")], FR-7) on the same controller, delegating all logic to IOfferingsService.
8. OfferingsModule (new — SlpModularCms.Modules.Offerings)
Purpose: IModule implementation, following the exact pattern of MasterModule/AvailabilityModule.
Responsibilities: RegisterServices — register OfferingsDbContext (MySQL, non-locking history repository per the existing convention), IOfferingRepository/OfferingRepository, IOfferingsService/OfferingsService. UseModule — apply pending OfferingsDbContext migrations at startup.
8 components: 1 shared hosting component (CmsHost), 1 new Client project, and 6 components making up the Offerings module (entity, DbContext, repository, service, controller, module registration).