Files
SluijsensandClaude Sonnet 5 cfb06b28b6
Continuous Integration / config (pull_request) Successful in 12s
Continuous Integration / changes (pull_request) Successful in 22s
Continuous Integration / backend-build (pull_request) Successful in 5m53s
Continuous Integration / vulnerability-scan (pull_request) Successful in 5m46s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m54s
Continuous Integration / backend-test (pull_request) Successful in 7m37s
Continuous Integration / frontend-build (pull_request) Successful in 2m14s
Continuous Integration / frontend-test (pull_request) Successful in 4m59s
Continuous Integration / frontend-lint (pull_request) Successful in 2m2s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 7m34s
Continuous Integration / deploy-test (pull_request) Skipped
Adds the Offerings module and retargets the CI/CD pipeline to Api.SlpSoftware
Implements Unit 2 "Offerings" (backend module, admin CRUD UI with
drag-and-drop reordering, public GET /api/v1/offerings endpoint) and
executes the feature's D-15 CI/CD cutover, switching the deploy
pipeline's build/publish target from SlpModularCms.Api to
SlpModularCms.Api.SlpSoftware.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
2026-08-02 16:23:09 +02:00

3.8 KiB

Domain Entities — Unit: Offerings

Entity Relationships

%%{init: {'themeVariables': {'primaryTextColor':'#000000','textColor':'#000000','tertiaryTextColor':'#000000'}}}%%
graph TD
    offering["Offering<br/>(entity)"]
    public_dto["OfferingDto<br/>(public contract, FR-6)"]
    admin_dto["OfferingAdminDto<br/>(admin view, FR-7)"]

    offering -->|"projected to (excludes IsDeleted/DeletedAt)"| public_dto
    offering -->|"projected to (adds DisplayOrder)"| admin_dto

    classDef entity fill:#bee3f8,stroke:#0d47a1,stroke-width:2px,color:#000000,font-weight:bold;
    classDef dto fill:#fed7aa,stroke:#e65100,stroke-width:2px,color:#000000,font-weight:bold;

    class offering entity;
    class public_dto,admin_dto dto;

Text alternative: the single Offering entity projects into two different DTO shapes — the public contract (no internal/administrative fields) and the admin view (adds DisplayOrder for the list UI) — blue for the stateful entity, orange for the two value-object projections.

Entity Definitions

Offering

Field Type Required Description
Id Guid Yes System-generated (Application Design Q4 = A). Public contract exposes this as a string.
Title string Yes Max 100 characters (Functional Design Q3 = A).
Description string Yes Max 500 characters.
Price string Yes Max 50 characters. Pre-formatted display string, not numeric (FR-5) — e.g. "€ 300" or "Op maat".
PriceNote string Yes Max 100 characters.
Features List<string> Yes Min 1, max 10 items; each item max 200 characters. Ordered — array order is display order.
CtaLabel string Yes Max 50 characters.
Featured bool No (default false) At most one non-deleted Offering may have this true at any time (US-10, enforced in IOfferingsService, not at the database/constraint level — see business-rules.md).
DisplayOrder int Yes Determines public array order and admin list order. Unique among non-deleted offerings; not necessarily contiguous after deletions (soft delete does not renumber).
IsDeleted bool Yes (default false) Soft-delete flag (Application Design Q5 = B). Never exposed on either DTO.
DeletedAt DateTimeOffset? No Set when IsDeleted becomes true. Never exposed on either DTO.
CreatedAt DateTimeOffset Yes Set once on creation. Part of the SECURITY-13 audit trail ("when").
UpdatedAt DateTimeOffset Yes Set on every create/update. Same rationale as CreatedAt.
LastModifiedByUserId Guid Yes The authenticated admin's user id, set on every create/update/delete (NFR Requirements Q3 = A). Closes the "who" half of the SECURITY-13 open item alongside CreatedAt/UpdatedAt's "when" — together a minimal audit trail without building a full audit-log table.

OfferingDto (public contract, FR-6)

Field Type Maps From
id string Offering.Id.ToString()
title string Offering.Title
description string Offering.Description
price string Offering.Price
priceNote string Offering.PriceNote
features string[] Offering.Features
ctaLabel string Offering.CtaLabel
featured bool Offering.Featured

OfferingAdminDto (admin view, FR-7)

Same fields as OfferingDto, plus:

Field Type Maps From
displayOrder int Offering.DisplayOrder

IsDeleted/DeletedAt/CreatedAt/UpdatedAt/LastModifiedByUserId are intentionally not exposed on either DTO — soft-deleted rows are never returned to any caller (repository-level filtering, per services.md), and the audit fields exist for potential future audit tooling, not for display in this unit's admin UI.