# Component Methods — SlpSoftware Production API **Note**: Signatures and high-level purpose only. Detailed business rules (exact exclusivity algorithm, validation rules, reorder edge cases) are defined in Functional Design for the relevant unit (CONSTRUCTION phase). ## `CmsHost` (static class, `SlpModularCms.Core.Hosting`) | Method | Input | Output | Purpose | |---|---|---|---| | `ConfigureServices` | `WebApplicationBuilder builder` | `ModuleOrchestrator` | Runs logging/Sentry setup, discovers and registers module services, registers core infrastructure (CORS, rate limiting, health checks, security headers, observability, Data Protection), and configures MVC controllers. Returns the `ModuleOrchestrator` instance so the caller can pass it into `ConfigurePipeline` after `builder.Build()`. | | `ConfigurePipeline` | `WebApplication app`, `ModuleOrchestrator orchestrator` | `void` | Runs the Core DB migration, wires the exception handler, security headers, rate limiter, Development-only OpenAPI/Scalar, HTTPS redirection, static content + SPA fallbacks, CORS, module middleware (`orchestrator.UseModules`), authentication/authorization, controller mapping, health checks, and the Sentry tunnel — in the exact order `Api/Program.cs` uses today, since that order encodes real constraints (documented as code comments in the current `Program.cs`). | Each project's `Program.cs` becomes: ```csharp var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true); var orchestrator = CmsHost.ConfigureServices(builder); var app = builder.Build(); CmsHost.ConfigurePipeline(app, orchestrator); app.Run(); ``` --- ## `IOfferingRepository` (`SlpModularCms.Modules.Offerings.Repositories`) | Method | Input | Output | Purpose | |---|---|---|---| | `GetAllAsync` | — | `IReadOnlyList` | All non-deleted offerings, ordered by `DisplayOrder`. | | `GetByIdAsync` | `Guid id` | `Offering?` | Single non-deleted offering, or `null`. | | `AddAsync` | `Offering offering` | `Offering` | Persist a new offering. | | `UpdateAsync` | `Offering offering` | `Offering` | Persist changes to an existing offering. | | `GetMaxDisplayOrderAsync` | — | `int` | Used by the service to append new offerings at the end of the display order. | | `GetFeaturedAsync` | — | `Offering?` | The currently-featured offering (if any), used to enforce exclusivity. | --- ## `IOfferingsService` (`SlpModularCms.Modules.Offerings.Services`) | Method | Input | Output | Purpose | |---|---|---|---| | `GetPublicOfferingsAsync` | — | `IReadOnlyList` | Backs FR-6 / US-01, US-02, US-03. | | `GetAllForAdminAsync` | — | `IReadOnlyList` | Backs the admin list view. | | `CreateAsync` | `CreateOfferingRequest request` | `OfferingAdminDto` | Backs US-04. Assigns a new `Id` (Q4 = A) and appends to the end of `DisplayOrder`. If `request.Featured` is `true`, un-features the previously-featured offering (US-10). | | `UpdateAsync` | `Guid id`, `UpdateOfferingRequest request` | `OfferingAdminDto` | Backs US-05. Same featured-exclusivity handling as `CreateAsync` when `request.Featured` is `true`. | | `DeleteAsync` | `Guid id` | `void` | Backs US-06/US-07. Soft-delete (Q5 = B) — always allowed, including for the last remaining offering. | | `ReorderAsync` | `IReadOnlyList orderedIds` | `void` | Backs US-08 (drag-and-drop). Full-list reorder — reassigns `DisplayOrder` to match the given sequence. | | `MoveUpAsync` | `Guid id` | `void` | Backs US-09. Swaps `DisplayOrder` with the immediately preceding offering. | | `MoveDownAsync` | `Guid id` | `void` | Backs US-09. Swaps `DisplayOrder` with the immediately following offering. | --- ## `OfferingsController` (`SlpModularCms.Modules.Offerings.Controllers`) | Action | Route | Auth | Input | Output | Purpose | |---|---|---|---|---|---| | `GetOfferings` | `GET /api/v1/offerings` | `[AllowAnonymous]` | — | `200 OK`, `OfferingDto[]` | FR-6 | | `GetAllForAdmin` | `GET /api/v1/offerings/admin` | `AdminOnly` | — | `200 OK`, `OfferingAdminDto[]` | Admin list view | | `Create` | `POST /api/v1/offerings/admin` | `AdminOnly` | `CreateOfferingRequest` | `201 Created`, `OfferingAdminDto` | US-04 | | `Update` | `PUT /api/v1/offerings/admin/{id}` | `AdminOnly` | `UpdateOfferingRequest` | `200 OK`, `OfferingAdminDto` | US-05 | | `Delete` | `DELETE /api/v1/offerings/admin/{id}` | `AdminOnly` | — | `204 No Content` | US-06/US-07 | | `Reorder` | `PUT /api/v1/offerings/admin/reorder` | `AdminOnly` | `ReorderOfferingsRequest` (ordered `Guid[]`) | `204 No Content` | US-08 | | `MoveUp` | `POST /api/v1/offerings/admin/{id}/move-up` | `AdminOnly` | — | `204 No Content` | US-09 | | `MoveDown` | `POST /api/v1/offerings/admin/{id}/move-down` | `AdminOnly` | — | `204 No Content` | US-09 | **Note**: Exact route naming (e.g. `/admin` suffix vs. a route-group prefix) may be refined in Functional Design or Code Generation Planning; the split shown here keeps the public route exactly as FR-6 specifies while keeping admin routes obviously distinct, consistent with decision Q3 (single controller, per-action authorization). --- ## `OfferingsModule` (`SlpModularCms.Modules.Offerings`) | Method | Input | Output | Purpose | |---|---|---|---| | `RegisterServices` | `IServiceCollection services` | `void` | Registers `OfferingsDbContext` (MySQL), `IOfferingRepository`/`OfferingRepository`, `IOfferingsService`/`OfferingsService`. | | `UseModule` | `IApplicationBuilder app` | `void` | Applies pending `OfferingsDbContext` migrations. |