Status: Third governance loop implemented for
AchievementReportContent;exam-sprint-domainno longer depends on Jackson /JsonNode. Remaining content debt is modelingOUTLOOKand retiring transitional unmodeled content.
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.
The repository is a Java 17, Spring Boot 3.3.5, Maven multi-module project with this ability layout:
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:
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;adapter.http rather than being mixed into domain code.The main governance items are:
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.Domain classes should answer: “what is this in business terms?” Examples:
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.
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.
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.
The infrastructure module may use technical names because its job is to adapt concrete technology. Prefixes such as AzureBlob, Playwright, Classpath, and InMemory are appropriate there.
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.
Long-term target:
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:
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
Recommended names:
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:
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.
Recommended names:
CreateExamSprintReportUseCase
GenerateExamSprintReportUseCase
GetExamSprintReportUseCase
DownloadExamSprintReportUseCase
ExpireExamSprintReportsUseCase
CreateExamSprintReportCommand
ExamSprintReportDetailResult
ReportFileStorage
ReportDocumentRenderer
PdfDocumentGenerator
ReportGenerationRequestPublisher
Preferred suffixes:
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.
Recommended names:
CreateExamSprintReportRequest
CreateExamSprintReportResponse
CreateExamSprintReportWithUrlResponse
ExamSprintReportDetailResponse
OutlookExamSprintReportPayload
AchievementExamSprintReportPayload
ExamSprintReportType
ExamSprintReportGenerationStatus
Allowed suffixes:
Request
Response
Payload
Type
Status
Rules:
Recommended names:
AzureBlobReportFileStorage
InMemoryExamSprintReportRepository
ClasspathOutlookReportDocumentRenderer
ClasspathAchievementReportDocumentRenderer
PlaywrightDocumentGenerator
ExamSprintReportInfrastructureConfiguration
Allowed technology prefixes:
AzureBlob
Playwright
Classpath
InMemory
Jdbc
Jpa
Redis
Kafka
Rabbit
Infrastructure names should make the chosen technology visible.
Goal: establish rules and stop new violations.
Actions:
ReportGenerationStatus.Acceptance criteria:
contracts or JsonNode without a reviewed exception;Goal: make domain independent from contracts and reduce JSON leakage.
Actions:
domain and map at boundaries.JsonNode payload usage with strongly named content concepts for at least one report type.ExamSprintReportStorage, ExamSprintReportRenderer, and ExamSprintReportPdfGenerator as application ports unless the team explicitly documents domain ownership.DefaultExamSprintReportApplicationService by extracting use cases behind the existing facade.Acceptance criteria:
exam-sprint-domain no longer depends on exam-sprint-contracts;Goal: establish a stable DDD/Clean Architecture template for future abilities.
Actions:
ExamSprintReport should become ExamSprintReportGeneration.GeneratedReportFile, ReportGenerationFailure, and report-content value objects when their behavior becomes stable.exam-sprint structure as the standard template for new abilities/{ability-name} modules.Acceptance criteria:
| 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 |
Use this list for every PR that touches abilities/exam-sprint.
Manager, Processor, Handler, Pipeline, Worker, Dispatcher, or Scheduler, is the reason documented?JsonNode, Map<String,Object>, or raw JSON entering domain logic?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 |
The recommended first loop is:
domain dependency baseline + ReportGenerationStatus migration
Why this loop first:
domain -> contracts coupling without changing the external API;ReportType migration and still applicable to payload migrations;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:
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