6 min readCalm pace · scan the outline anytime

Portable requirements

MUST-level Modular Hexagonal DDD requirements for any project adopting this architecture.

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

  1. Replaceable modules — swapping a peer for HTTP must not break the consumer’s Domain/Application compile surface.
  2. Strict Domain — no host framework, ORM, or foreign modules inside Domain.
  3. One golden flow — UI → Use Case → Domain → Ports; Infrastructure adapts outward.
  4. Quality by default — boundary tests + style/static analysis on the host adapter.
  5. 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)

LayerAllowed
DomainOwn Domain + Shared kernel ports only
Application Use CasesOwn Domain + Shared ports; no facades / ORM
Composition root (Providers)Host wiring only (bind, routes, config merge)
Infrastructure / UIFramework, ORM, HTTP, admin UI

Details: Strictness ladder.

FR-3 Cross-module (MUST)

Only:

  1. Domain Events (async) → consumer Infrastructure translation listener → consumer Use Case
  2. 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}/SomethingUseCase with __invoke
  • Nested helpers: no UseCase suffix; not called from UI
  • Mirror folders for DTOs and Ports
  • Application DTO = UI input; Domain DTO = intra-module

FR-5 Shared promote rule (MUST)

CaseLocation
Tech used by one moduleThat module’s Infrastructure
Same tech used by ≥2 modulesPromote to Shared
Cross-module business conceptNever 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)

IDRequirement
QR-1Automated tests for every behaviour change (prefer feature; unit for pure Domain)
QR-2Architecture 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-3Host formatter run before finalize (tool is host-specific)
QR-4Static analysis clean on changed module code
QR-5Public module APIs documented; architecture lives on this site + host adapter pages
QR-6Event consumers covered for duplicate eventId (idempotency)

Boundary tests assert (portable):

  1. No use of another module’s Application/Domain from this module’s Application/Domain.
  2. No peer *ModuleInterface injected into Use Cases.
  3. No ORM / host facade usage in Domain (and Application Use Cases).
  4. 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

Next

Modular Hexagonal Domain-Driven Design
Core 1.0.0-draft