# DDD Naming and Architecture Governance Design

> Status: Third governance loop implemented for `AchievementReportContent`; `exam-sprint-domain` no longer depends on Jackson / `JsonNode`. Remaining content debt is modeling `OUTLOOK` and retiring transitional unmodeled content.

## 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 items are:

- historical `exam-sprint-domain -> exam-sprint-contracts` coupling has been cleared and should remain guarded by architecture tests;
- `OUTLOOK` report content remains transitional through unmodeled boundary-prepared content;
- `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 | Status / current reason | Exit condition or guardrail |
| --- | --- | --- |
| `domain -> contracts` | Cleared in the `ReportType` loop after `ReportGenerationStatus` and `ReportType` became domain-owned | keep ArchUnit hard rule; map future contract/domain duplicates in application |
| `domain -> jackson` | Cleared at compile-time in the `AchievementReportContent` loop by replacing domain `JsonNode` with `ReportContent`; `OUTLOOK` still uses a transitional unmodeled content wrapper prepared at application boundary | introduce `OutlookReportContent`, remove `UnmodeledReportContent`, and keep ArchUnit hard rule |
| Payload records contain business behavior | `AchievementReportContent` now owns the selected achievement content shape; `OutlookExamSprintReportPayload` and richer invariants remain in contracts/infrastructure | migrate `OutlookReportContent` and move stable invariants into domain value objects incrementally |
| `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<String,Object>`, 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 created a repeatable mapper pattern later used for the `ReportType` migration and still applicable to 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, and architecture tests established the initial dependency baseline.

Second loop result: `ReportType` is now domain-owned; the application boundary maps it to and from public `ExamSprintReportType`; `exam-sprint-domain` no longer depends on `exam-sprint-contracts`; and the domain-to-contract architecture rule is a hard guardrail.

Third loop result: `AchievementReportContent` is introduced as strongly named domain report content; the application boundary converts public achievement payload JSON/contracts DTOs into domain content; domain main source no longer imports Jackson / `JsonNode`; and the domain-to-Jackson architecture rule is tightened to a hard guardrail.

Remaining payload debt: `OUTLOOK` content remains unmodeled through a transitional `UnmodeledReportContent` wrapper so this loop does not migrate all payload records or rewrite the larger outlook renderer.

Next recommended loop: introduce `OutlookReportContent` and retire `UnmodeledReportContent`, then review whether rendering/file/PDF ports should move to application in a separate dedicated loop.

The detailed implementation plan is in:

```text
docs/superpowers/plans/2026-04-27-ddd-naming-governance-first-loop.md
docs/superpowers/plans/2026-04-28-ddd-naming-governance-report-type-loop.md
docs/superpowers/plans/2026-04-28-ddd-naming-governance-jsonnode-payload-loop.md
```
