[{"data":1,"prerenderedAt":651},["ShallowReactive",2],{"page-\u002Fv1\u002Fcore\u002Frequirements":3},{"id":4,"title":5,"body":6,"description":643,"extension":644,"meta":645,"navigation":646,"path":647,"seo":648,"stem":649,"__hash__":650},"content\u002Fv1\u002Fcore\u002Frequirements.md","Portable requirements",{"type":7,"value":8,"toc":622},"minimark",[9,13,37,50,55,58,74,78,111,115,138,142,147,157,167,171,227,234,238,241,266,269,277,281,332,336,361,365,407,411,434,438,446,450,462,466,545,550,575,582,586,597,601],[10,11,5],"h1",{"id":12},"portable-requirements",[14,15,16,17,21,22,26,27,26,30,26,33,36],"p",{},"These are the ",[18,19,20],"strong",{},"normative MUST"," rules for adopting Modular Hexagonal Domain-Driven Design. They are domain-agnostic. Rename illustrative contexts (",[23,24,25],"code",{},"Ordering",", ",[23,28,29],{},"Warehouse",[23,31,32],{},"Directory",[23,34,35],{},"Identity",") to your bounded contexts; keep the rules.",[14,38,39,40,43,44,49],{},"This documentation site is the ",[18,41,42],{},"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 ",[45,46,48],"a",{"href":47},"\u002Fv1\u002Fadapters","Adapters",".",[51,52,54],"h2",{"id":53},"problem-this-standard-solves","Problem this standard solves",[14,56,57],{},"Without a shared shape, codebases drift into:",[59,60,61,65,68,71],"ul",{},[62,63,64],"li",{},"Controllers \u002F admin UI \u002F jobs that reach persistence across “modules”",[62,66,67],{},"Shared “god” helpers that couple business domains",[62,69,70],{},"Integrations that cannot be replaced without rewriting Use Cases",[62,72,73],{},"Inconsistent module layouts across contributors and sessions",[51,75,77],{"id":76},"goals","Goals",[79,80,81,87,93,99,105],"ol",{},[62,82,83,86],{},[18,84,85],{},"Replaceable modules"," — swapping a peer for HTTP must not break the consumer’s Domain\u002FApplication compile surface.",[62,88,89,92],{},[18,90,91],{},"Strict Domain"," — no host framework, ORM, or foreign modules inside Domain.",[62,94,95,98],{},[18,96,97],{},"One golden flow"," — UI → Use Case → Domain → Ports; Infrastructure adapts outward.",[62,100,101,104],{},[18,102,103],{},"Quality by default"," — boundary tests + style\u002Fstatic analysis on the host adapter.",[62,106,107,110],{},[18,108,109],{},"Tool neutrality"," — the standard does not require a specific IDE, AI product, or any AI tooling.",[51,112,114],{"id":113},"non-goals","Non-goals",[59,116,117,125,128,135],{},[62,118,119,120,124],{},"Microservices on day one (design modules as if they ",[121,122,123],"em",{},"may"," become packages\u002FAPIs later).",[62,126,127],{},"Dumping every reusable idea into Shared “just in case”.",[62,129,130,131,134],{},"Putting product feature backlog inside architecture docs (use each project’s ",[23,132,133],{},"ROADMAP.md",").",[62,136,137],{},"Mandating Cursor or any other AI coding assistant.",[51,139,141],{"id":140},"functional-requirements","Functional requirements",[143,144,146],"h3",{"id":145},"fr-1-module-layout-must","FR-1 Module layout (MUST)",[148,149,155],"pre",{"className":150,"code":152,"language":153,"meta":154},[151],"language-text","{Module}\u002F\n  Application\u002F     UseCases, DTO, Providers (composition root)\n  Domain\u002F          Entities, Ports, Events, Enums, Domain DTOs\n  Infrastructure\u002F  Persistence adapters, ExternalServices (ACL), Config\n  UI\u002F              Controllers, Routes, validation, admin UI, console\nShared\u002F            Promoted technical ports\u002Fadapters only\n","text","",[23,156,152],{"__ignoreMap":154},[14,158,159,160,26,163,166],{},"Host roots vary (",[23,161,162],{},"app\u002FModules",[23,164,165],{},"src\u002FModules",", …). Roles do not.",[143,168,170],{"id":169},"fr-2-strictness-ladder-must","FR-2 Strictness ladder (MUST)",[172,173,174,187],"table",{},[175,176,177],"thead",{},[178,179,180,184],"tr",{},[181,182,183],"th",{},"Layer",[181,185,186],{},"Allowed",[188,189,190,199,207,219],"tbody",{},[178,191,192,196],{},[193,194,195],"td",{},"Domain",[193,197,198],{},"Own Domain + Shared kernel ports only",[178,200,201,204],{},[193,202,203],{},"Application Use Cases",[193,205,206],{},"Own Domain + Shared ports; no facades \u002F ORM",[178,208,209,212],{},[193,210,211],{},"Composition root (Providers)",[193,213,214,215,218],{},"Host wiring only (",[23,216,217],{},"bind",", routes, config merge)",[178,220,221,224],{},[193,222,223],{},"Infrastructure \u002F UI",[193,225,226],{},"Framework, ORM, HTTP, admin UI",[14,228,229,230,49],{},"Details: ",[45,231,233],{"href":232},"\u002Fv1\u002Fcore\u002Fstrictness-ladder","Strictness ladder",[143,235,237],{"id":236},"fr-3-cross-module-must","FR-3 Cross-module (MUST)",[14,239,240],{},"Only:",[79,242,243,253],{},[62,244,245,248,249,252],{},[18,246,247],{},"Domain Events"," (async) → consumer Infrastructure ",[18,250,251],{},"translation listener"," → consumer Use Case",[62,254,255,258,259,262,263],{},[18,256,257],{},"ACL"," (sync) → consumer ",[23,260,261],{},"*PortInterface"," + Infra ACL adapter → peer thin ",[23,264,265],{},"*ModuleInterface",[14,267,268],{},"Forbidden: Use Case A importing Use Case \u002F Entity \u002F Enum of module B.",[14,270,271,272,276],{},"Multi-step flows may use ",[45,273,275],{"href":274},"\u002Fv1\u002Fcore\u002Forchestration","orchestration"," but still only ACL + Events as bridges.",[143,278,280],{"id":279},"fr-3a-contracts-must","FR-3a Contracts (MUST)",[59,282,283,294,311,325],{},[62,284,285,286,289,290,134],{},"Peer façades are ",[18,287,288],{},"thin"," and HTTP-mappable (",[45,291,293],{"href":292},"\u002Fv1\u002Fcore\u002Fcross-module-contracts","contracts",[62,295,296,297,26,300,26,303,306,307,310],{},"Cross-module events carry at least ",[23,298,299],{},"eventId",[23,301,302],{},"occurredAt",[23,304,305],{},"schemaVersion",", and a ",[18,308,309],{},"rich happy-path"," payload.",[62,312,313,314,317,318,320,321,134],{},"Consumers are ",[18,315,316],{},"idempotent"," on ",[23,319,299],{}," (",[45,322,324],{"href":323},"\u002Fv1\u002Fcore\u002Fevent-delivery","event delivery",[62,326,327,328,134],{},"Do not span one DB transaction across two modules’ write models (",[45,329,331],{"href":330},"\u002Fv1\u002Fcore\u002Ftransactions-and-failures","transactions",[143,333,335],{"id":334},"fr-4-use-cases-must","FR-4 Use Cases (MUST)",[59,337,338,348,355,358],{},[62,339,340,341,344,345],{},"First-level: ",[23,342,343],{},"UseCases\u002F{Capability}\u002FSomethingUseCase"," with ",[23,346,347],{},"__invoke",[62,349,350,351,354],{},"Nested helpers: no ",[23,352,353],{},"UseCase"," suffix; not called from UI",[62,356,357],{},"Mirror folders for DTOs and Ports",[62,359,360],{},"Application DTO = UI input; Domain DTO = intra-module",[143,362,364],{"id":363},"fr-5-shared-promote-rule-must","FR-5 Shared promote rule (MUST)",[172,366,367,377],{},[175,368,369],{},[178,370,371,374],{},[181,372,373],{},"Case",[181,375,376],{},"Location",[188,378,379,387,395],{},[178,380,381,384],{},[193,382,383],{},"Tech used by one module",[193,385,386],{},"That module’s Infrastructure",[178,388,389,392],{},[193,390,391],{},"Same tech used by ≥2 modules",[193,393,394],{},"Promote to Shared",[178,396,397,404],{},[193,398,399,400,403],{},"Cross-module ",[18,401,402],{},"business"," concept",[193,405,406],{},"Never Shared — Event \u002F ACL",[143,408,410],{"id":409},"fr-6-persistence-must","FR-6 Persistence (MUST)",[59,412,413,416,419,422],{},[62,414,415],{},"Adapters map persistence records ↔ Domain entities",[62,417,418],{},"Never leak ORM models into Use Cases \u002F Domain",[62,420,421],{},"Avoid cross-module foreign keys that force deploy coupling (prefer codes + ACL)",[62,423,424,425,428,429,433],{},"Cross-context lists\u002Fdashboards use ACL reads or ",[18,426,427],{},"owned projections",", not ORM joins across modules (",[45,430,432],{"href":431},"\u002Fv1\u002Fcore\u002Freads-and-reporting","reads",")",[143,435,437],{"id":436},"fr-7-ui-must","FR-7 UI (MUST)",[59,439,440,443],{},[62,441,442],{},"Controllers \u002F admin \u002F jobs are adapters: authorize → DTO → Use Case → present",[62,444,445],{},"No business rules in UI closures beyond mapping and presentation",[143,447,449],{"id":448},"fr-8-versioning-must","FR-8 Versioning (MUST)",[59,451,452,459],{},[62,453,454,455,134],{},"Adopt Core from this site’s badge \u002F pinned revision (",[45,456,458],{"href":457},"\u002Fv1\u002Fcore\u002Fversioning","versioning",[62,460,461],{},"Treat façades and Domain Events as published contracts; prefer additive evolution.",[51,463,465],{"id":464},"quality-requirements-portable","Quality requirements (portable)",[172,467,468,478],{},[175,469,470],{},[178,471,472,475],{},[181,473,474],{},"ID",[181,476,477],{},"Requirement",[188,479,480,488,506,514,522,534],{},[178,481,482,485],{},[193,483,484],{},"QR-1",[193,486,487],{},"Automated tests for every behaviour change (prefer feature; unit for pure Domain)",[178,489,490,493],{},[193,491,492],{},"QR-2",[193,494,495,498,499,501,502,505],{},[18,496,497],{},"Architecture boundary tests"," fail CI if Application\u002FDomain import foreign modules \u002F peer ",[23,500,265],{}," \u002F ORM in inner layers — tool is host-specific; the ",[18,503,504],{},"assertions"," are Core MUST",[178,507,508,511],{},[193,509,510],{},"QR-3",[193,512,513],{},"Host formatter run before finalize (tool is host-specific)",[178,515,516,519],{},[193,517,518],{},"QR-4",[193,520,521],{},"Static analysis clean on changed module code",[178,523,524,527],{},[193,525,526],{},"QR-5",[193,528,529,530,533],{},"Public module APIs documented; architecture lives on ",[18,531,532],{},"this site"," + host adapter pages",[178,535,536,539],{},[193,537,538],{},"QR-6",[193,540,541,542,544],{},"Event consumers covered for duplicate ",[23,543,299],{}," (idempotency)",[14,546,547],{},[18,548,549],{},"Boundary tests assert (portable):",[79,551,552,559,565,568],{},[62,553,554,555,558],{},"No ",[23,556,557],{},"use"," of another module’s Application\u002FDomain from this module’s Application\u002FDomain.",[62,560,561,562,564],{},"No peer ",[23,563,265],{}," injected into Use Cases.",[62,566,567],{},"No ORM \u002F host facade usage in Domain (and Application Use Cases).",[62,569,570,571,574],{},"No literal ",[23,572,573],{},"Feature\u002F"," directory under UseCases \u002F DTO \u002F Ports.",[14,576,577,578,581],{},"Host-specific commands: each adapter’s ",[45,579,580],{"href":47},"Tooling"," page.",[51,583,585],{"id":584},"success-metrics","Success metrics",[59,587,588,591,594],{},[62,589,590],{},"New module scaffolded quickly with an arch-green empty tree",[62,592,593],{},"Peer swap (ACL → HTTP) requires Infrastructure-only changes in the consumer",[62,595,596],{},"Contributors follow the same layout without depending on a particular AI tool",[51,598,600],{"id":599},"next","Next",[59,602,603,609,615],{},[62,604,605],{},[45,606,608],{"href":607},"\u002Fv1\u002Fadopt","Adopt in another project",[62,610,611],{},[45,612,614],{"href":613},"\u002Fv1\u002Fcore\u002Fwhere-does-this-go","Where does this code go?",[62,616,617,621],{},[45,618,620],{"href":619},"\u002Fv1\u002Fcore\u002Fagent-pack","Optional assistant pack"," (not required)",{"title":154,"searchDepth":623,"depth":623,"links":624},2,[625,626,627,628,640,641,642],{"id":53,"depth":623,"text":54},{"id":76,"depth":623,"text":77},{"id":113,"depth":623,"text":114},{"id":140,"depth":623,"text":141,"children":629},[630,632,633,634,635,636,637,638,639],{"id":145,"depth":631,"text":146},3,{"id":169,"depth":631,"text":170},{"id":236,"depth":631,"text":237},{"id":279,"depth":631,"text":280},{"id":334,"depth":631,"text":335},{"id":363,"depth":631,"text":364},{"id":409,"depth":631,"text":410},{"id":436,"depth":631,"text":437},{"id":448,"depth":631,"text":449},{"id":464,"depth":623,"text":465},{"id":584,"depth":623,"text":585},{"id":599,"depth":623,"text":600},"MUST-level Modular Hexagonal DDD requirements for any project adopting this architecture.","md",{},true,"\u002Fv1\u002Fcore\u002Frequirements",{"title":5,"description":643},"v1\u002Fcore\u002Frequirements","5ucUiu7E8usWAW0wsi-vLCw5s2Fe008KHml9alTe_e0",1786241412686]