Portable requirements
These are the normative MUST rules for adopting Modular Hexagonal Domain-Driven Design. They are domain-agnostic. Rename illustrative contexts (Ordering, Warehouse, Directory, Identity) to your bounded contexts; keep the rules.
This documentation site is the sole architecture canon. Other repositories — including host demos and product apps — should link here instead of maintaining a divergent standard. Host stack details live under Adapters.
Problem this standard solves
Without a shared shape, codebases drift into:
- Controllers / admin UI / jobs that reach persistence across “modules”
- Shared “god” helpers that couple business domains
- Integrations that cannot be replaced without rewriting Use Cases
- Inconsistent module layouts across contributors and sessions
Goals
- Replaceable modules — swapping a peer for HTTP must not break the consumer’s Domain/Application compile surface.
- Strict Domain — no host framework, ORM, or foreign modules inside Domain.
- One golden flow — UI → Use Case → Domain → Ports; Infrastructure adapts outward.
- Quality by default — boundary tests + style/static analysis on the host adapter.
- Tool neutrality — the standard does not require a specific IDE, AI product, or any AI tooling.
Non-goals
- Microservices on day one (design modules as if they may become packages/APIs later).
- Dumping every reusable idea into Shared “just in case”.
- Putting product feature backlog inside architecture docs (use each project’s
ROADMAP.md). - Mandating Cursor or any other AI coding assistant.
Functional requirements
FR-1 Module layout (MUST)
{Module}/
Application/ UseCases, DTO, Providers (composition root)
Domain/ Entities, Ports, Events, Enums, Domain DTOs
Infrastructure/ Persistence adapters, ExternalServices (ACL), Config
UI/ Controllers, Routes, validation, admin UI, console
Shared/ Promoted technical ports/adapters only
Host roots vary (app/Modules, src/Modules, …). Roles do not.
FR-2 Strictness ladder (MUST)
| Layer | Allowed |
|---|---|
| Domain | Own Domain + Shared kernel ports only |
| Application Use Cases | Own Domain + Shared ports; no facades / ORM |
| Composition root (Providers) | Host wiring only (bind, routes, config merge) |
| Infrastructure / UI | Framework, ORM, HTTP, admin UI |
Details: Strictness ladder.
FR-3 Cross-module (MUST)
Only:
- Domain Events (async) → consumer Infrastructure translation listener → consumer Use Case
- ACL (sync) → consumer
*PortInterface+ Infra ACL adapter → peer thin*ModuleInterface
Forbidden: Use Case A importing Use Case / Entity / Enum of module B.
Multi-step flows may use orchestration but still only ACL + Events as bridges.
FR-3a Contracts (MUST)
- Peer façades are thin and HTTP-mappable (contracts).
- Cross-module events carry at least
eventId,occurredAt,schemaVersion, and a rich happy-path payload. - Consumers are idempotent on
eventId(event delivery). - Do not span one DB transaction across two modules’ write models (transactions).
FR-4 Use Cases (MUST)
- First-level:
UseCases/{Capability}/SomethingUseCasewith__invoke - Nested helpers: no
UseCasesuffix; not called from UI - Mirror folders for DTOs and Ports
- Application DTO = UI input; Domain DTO = intra-module
FR-5 Shared promote rule (MUST)
| Case | Location |
|---|---|
| Tech used by one module | That module’s Infrastructure |
| Same tech used by ≥2 modules | Promote to Shared |
| Cross-module business concept | Never Shared — Event / ACL |
FR-6 Persistence (MUST)
- Adapters map persistence records ↔ Domain entities
- Never leak ORM models into Use Cases / Domain
- Avoid cross-module foreign keys that force deploy coupling (prefer codes + ACL)
- Cross-context lists/dashboards use ACL reads or owned projections, not ORM joins across modules (reads)
FR-7 UI (MUST)
- Controllers / admin / jobs are adapters: authorize → DTO → Use Case → present
- No business rules in UI closures beyond mapping and presentation
FR-8 Versioning (MUST)
- Adopt Core from this site’s badge / pinned revision (versioning).
- Treat façades and Domain Events as published contracts; prefer additive evolution.
Quality requirements (portable)
| ID | Requirement |
|---|---|
| QR-1 | Automated tests for every behaviour change (prefer feature; unit for pure Domain) |
| QR-2 | Architecture boundary tests fail CI if Application/Domain import foreign modules / peer *ModuleInterface / ORM in inner layers — tool is host-specific; the assertions are Core MUST |
| QR-3 | Host formatter run before finalize (tool is host-specific) |
| QR-4 | Static analysis clean on changed module code |
| QR-5 | Public module APIs documented; architecture lives on this site + host adapter pages |
| QR-6 | Event consumers covered for duplicate eventId (idempotency) |
Boundary tests assert (portable):
- No
useof another module’s Application/Domain from this module’s Application/Domain. - No peer
*ModuleInterfaceinjected into Use Cases. - No ORM / host facade usage in Domain (and Application Use Cases).
- No literal
Feature/directory under UseCases / DTO / Ports.
Host-specific commands: each adapter’s Tooling page.
Success metrics
- New module scaffolded quickly with an arch-green empty tree
- Peer swap (ACL → HTTP) requires Infrastructure-only changes in the consumer
- Contributors follow the same layout without depending on a particular AI tool