# DDD Naming and Architecture Governance Design > Status: First governance loop implemented; remaining `domain -> contracts` debt is `ReportType`. The next `ReportType` loop is recommended but not yet implemented. ## Goal Establish a long-term governance model for DDD-aligned naming, module dependencies, and architectural boundaries in `dcjxb.microservice`, starting with the existing `exam-sprint` ability and then turning the resulting rules into a repeatable template for future abilities. ## Current Context The repository is a Java 17, Spring Boot 3.3.5, Maven multi-module project with this ability layout: ```text ability-center-runtime ability-center-kernel abilities/exam-sprint/contracts abilities/exam-sprint/application abilities/exam-sprint/domain abilities/exam-sprint/infrastructure ``` The project already has a useful DDD/Clean Architecture foundation: - code is grouped by the `exam-sprint` business ability; - `contracts`, `application`, `domain`, and `infrastructure` are separate Maven modules; - `ExamSprintReportRepository` is defined in `domain` and implemented in `infrastructure`; - `ExamSprintReport` contains lifecycle behavior instead of being a pure data object; - HTTP adapters live under `adapter.http` rather than being mixed into domain code. The main governance gaps are: - `exam-sprint-domain` depends on `exam-sprint-contracts`; - domain classes use `JsonNode` as report payload representation; - `contracts` payload classes contain many business concepts and value-object candidates; - `ExamSprintReport` mixes report, generation task, generated file, and status-record semantics; - `Pipeline`, `Worker`, `Dispatcher`, and `Scheduler` naming emphasizes execution mechanics over use cases; - `Storage`, `Renderer`, and `PdfGenerator` currently sit in `domain` even though they mostly describe technical delivery mechanisms. ## Governance Principles ### 1. Domain owns the ubiquitous language Domain classes should answer: “what is this in business terms?” Examples: ```text ExamSprintReportGeneration ReportGenerationStatus GeneratedReportFile ReadinessAssessment SyllabusMastery FrequencyPlan ScoreImprovementCase ``` Domain classes should not be named after transport or implementation concepts such as `Payload`, `JsonNode`, `Response`, `storageObjectKey`, `pdfBytes`, or `htmlContent`. ### 2. Contracts describe external protocol only The `contracts` module owns external request/response shapes and API-facing enums. Contracts may contain validation annotations and JSON-friendly structure, but domain code must not depend on contract DTOs. ### 3. Application names use cases and ports The `application` module owns use-case orchestration and outbound port definitions. Its primary names should be `UseCase`, `Command`, `Query`, `Result`, `ApplicationService`, and business-named ports. ### 4. Infrastructure names implementation technology The `infrastructure` module may use technical names because its job is to adapt concrete technology. Prefixes such as `AzureBlob`, `OpenHtmlToPdf`, `Classpath`, and `InMemory` are appropriate there. ### 5. Governance is incremental Existing debt should be baselined and reduced in small vertical slices. New code must follow the rules immediately. Historical exceptions need owners, expiry expectations, and clear exit criteria. ## Target Module Dependencies Long-term target: ```text runtime -> contracts runtime -> application runtime -> infrastructure infrastructure -> application infrastructure -> domain application -> domain contracts -> kernel / validation api domain -> kernel, only for stable domain-agnostic primitives ``` Forbidden long-term dependencies: ```text domain -> contracts domain -> application domain -> infrastructure domain -> runtime domain -> jackson domain -> spring domain -> jakarta.validation application -> infrastructure implementation classes contracts -> domain/application/infrastructure/runtime infrastructure -> runtime/adapter.http/controller ``` ## Naming Rules ### Domain Recommended names: ```text ExamSprintReportGeneration ReportGenerationStatus ReportType GeneratedReportFile ReportRetentionPolicy ReportFailureReason OutlookReportContent AchievementReportContent ReadinessAssessment SyllabusMastery FrequencyPlan ScoreImprovementCase ExamSprintReportRepository ``` Allowed suffixes: | Concept | Preferred naming | | --- | --- | | Repository | `Repository` | | Domain service | `DomainService`, only when behavior does not belong naturally on an entity/value object | | Domain event | `Event` | | Business rule | `Policy` or `Specification` | | Identifier | `Id` | | Lifecycle state | `Status` | | Business category | `Type` | Avoid in domain: ```text DTO Request Response Payload Controller Manager Processor Handler Pipeline Worker Dispatcher Scheduler JsonNode Storage Renderer PdfGenerator ``` `Storage`, `Renderer`, and `PdfGenerator` may only remain in domain if the team explicitly decides they are domain capabilities rather than technical delivery mechanisms. ### Application Recommended names: ```text CreateExamSprintReportUseCase GenerateExamSprintReportUseCase GetExamSprintReportUseCase DownloadExamSprintReportUseCase ExpireExamSprintReportsUseCase CreateExamSprintReportCommand ExamSprintReportDetailResult ReportFileStorage ReportDocumentRenderer PdfDocumentGenerator ReportGenerationRequestPublisher ``` Preferred suffixes: ```text UseCase ApplicationService Command Query Result Port Executor Publisher Mapper Job ScheduledJob ``` Avoid using `Pipeline`, `Worker`, `Dispatcher`, and `Scheduler` for new business-flow classes. If execution mechanics are the actual concept, place the class in a technical package and document the reason in review. ### Contracts Recommended names: ```text CreateExamSprintReportRequest CreateExamSprintReportResponse CreateExamSprintReportWithUrlResponse ExamSprintReportDetailResponse OutlookExamSprintReportPayload AchievementExamSprintReportPayload ExamSprintReportType ExamSprintReportGenerationStatus ``` Allowed suffixes: ```text Request Response Payload Type Status ``` Rules: - contracts DTOs must not be used as domain models; - contracts enums that duplicate domain concepts must be mapped explicitly; - nested payload records that contain business behavior must be registered as domain value-object candidates. ### Infrastructure Recommended names: ```text AzureBlobReportFileStorage InMemoryExamSprintReportRepository ClasspathOutlookReportDocumentRenderer ClasspathAchievementReportDocumentRenderer OpenHtmlToPdfDocumentGenerator ExamSprintReportInfrastructureConfiguration ``` Allowed technology prefixes: ```text AzureBlob OpenHtmlToPdf Classpath InMemory Jdbc Jpa Redis Kafka Rabbit ``` Infrastructure names should make the chosen technology visible. ## Phased Roadmap ### Short term: 1-2 weeks Goal: establish rules and stop new violations. Actions: 1. Add this governance document to the repository. 2. Add a first architecture test baseline with allowlisted existing debt. 3. Start PR checklist enforcement for naming and dependency direction. 4. Move one lifecycle concept from contracts ownership into domain ownership; recommended first candidate: `ReportGenerationStatus`. 5. Keep external API responses unchanged by mapping domain status back to contract status in application code. Acceptance criteria: - new domain code cannot introduce new dependencies on `contracts` or `JsonNode` without a reviewed exception; - at least one existing domain-to-contract dependency is removed; - architecture tests run in CI or in the normal Maven test command; - external HTTP contract remains compatible. ### Mid term: 1-2 months Goal: make domain independent from contracts and reduce JSON leakage. Actions: 1. Move or duplicate domain-owned enums into `domain` and map at boundaries. 2. Replace domain `JsonNode payload` usage with strongly named content concepts for at least one report type. 3. Move business invariants from payload records into domain value objects. 4. Reclassify `ExamSprintReportStorage`, `ExamSprintReportRenderer`, and `ExamSprintReportPdfGenerator` as application ports unless the team explicitly documents domain ownership. 5. Start thinning `DefaultExamSprintReportApplicationService` by extracting use cases behind the existing facade. Acceptance criteria: - `exam-sprint-domain` no longer depends on `exam-sprint-contracts`; - new domain code does not use Jackson types; - at least one report content payload has a domain model; - application ports own rendering, PDF, and file-storage abstractions if they are technical delivery mechanisms. ### Long term: 1-2 quarters Goal: establish a stable DDD/Clean Architecture template for future abilities. Actions: 1. Decide whether the current `ExamSprintReport` should become `ExamSprintReportGeneration`. 2. Extract `GeneratedReportFile`, `ReportGenerationFailure`, and report-content value objects when their behavior becomes stable. 3. Split application orchestration into use cases: create, generate, get, download, and expire. 4. Convert architecture rules from allowlist-based soft governance to hard CI checks. 5. Publish the `exam-sprint` structure as the standard template for new `abilities/{ability-name}` modules. Acceptance criteria: - domain contains business language and business rules only; - application contains use cases and ports; - contracts contains external API protocol only; - infrastructure contains concrete adapters only; - architectural allowlist trends toward zero; - new ability modules follow the same pattern from creation. ## Technical Debt Register | Debt | Current reason | Exit condition | | --- | --- | --- | | `domain -> contracts` | `ReportType` still reuses API enum after `ReportGenerationStatus` migration | domain-owned `ReportType` plus application mapper | | `domain -> jackson` | `ExamSprintReport` and renderer use `JsonNode` payload | domain uses strongly named content/value objects | | Payload records contain business behavior | request payloads currently host consistency methods | invariants move to domain value objects | | `ExamSprintReport` has mixed semantics | current model stores generation state and file references | model is renamed or split around generation semantics | | domain hosts `Storage`/`Renderer`/`PdfGenerator` | ports were initially placed near report aggregate | ports move to application or are explicitly documented as domain capabilities | | application contains technical-flow names | existing pipeline/worker/dispatcher naming | new use-case names introduced and old names retired gradually | ## Review Checklist Use this list for every PR that touches `abilities/exam-sprint`. ### Dependencies - [ ] Did domain add a dependency on contracts? - [ ] Did domain add Jackson, Spring, validation, Azure, PDF, or persistence dependencies? - [ ] Did application depend on infrastructure implementation classes? - [ ] Did contracts depend on domain, application, infrastructure, or runtime? - [ ] Did infrastructure depend on runtime, controller, or HTTP adapter classes? ### Naming - [ ] Does each new domain name describe a business concept? - [ ] Does each new application name describe a use case, command, result, or port? - [ ] Does each new contract name clearly describe request/response/payload protocol? - [ ] Does each new infrastructure name describe concrete technology or adapter behavior? - [ ] If a new class uses `Manager`, `Processor`, `Handler`, `Pipeline`, `Worker`, `Dispatcher`, or `Scheduler`, is the reason documented? ### Model boundaries - [ ] Is an API DTO being used as a domain model? - [ ] Is `JsonNode`, `Map`, or raw JSON entering domain logic? - [ ] Is a new string field actually a value-object candidate? - [ ] Is a business invariant implemented outside domain without a reason? - [ ] Are lifecycle transitions controlled by domain methods? ### Compatibility and tests - [ ] Does the public API remain compatible? - [ ] Are contracts mapped explicitly when domain concepts differ? - [ ] Are domain/application tests updated for success and failure paths? - [ ] Are architecture tests updated when dependency rules change? - [ ] Is any allowlist entry reduced, updated, or justified? ## Metrics Track these metrics once per iteration: | Metric | Target | | --- | --- | | `domain -> contracts` references | 0 | | `domain -> jackson` references | 0 | | `JsonNode` usages in domain | 0 | | new domain classes ending in `DTO`, `Request`, `Response`, or `Payload` | 0 | | architecture allowlist entries | decreasing | | new architectural violations | 0 | | application classes with broad orchestration responsibilities | decreasing | | payload nested records with business behavior | decreasing or explicitly accepted | ## First Governance Loop The recommended first loop is: ```text domain dependency baseline + ReportGenerationStatus migration ``` Why this loop first: - generation status is a lifecycle concept and belongs to domain language; - it reduces `domain -> contracts` coupling without changing the external API; - it creates a repeatable mapper pattern for future `ReportType` and payload migrations; - it gives the architecture tests a concrete baseline and a concrete debt reduction. First loop result: `ReportGenerationStatus` is now domain-owned; the application boundary maps it back to public contracts status, architecture tests baseline the remaining debt, and the next `ReportType` loop has not yet been implemented. Next recommended loop: migrate `ReportType` into domain, remove `exam-sprint-domain` dependency on `exam-sprint-contracts`, and tighten the architecture allowlist accordingly. The detailed implementation plan is in: ```text docs/superpowers/plans/2026-04-27-ddd-naming-governance-first-loop.md ```