[{"data":1,"prerenderedAt":436},["ShallowReactive",2],{"page-\u002Fv1\u002Fcore\u002Fevent-delivery":3},{"id":4,"title":5,"body":6,"description":428,"extension":429,"meta":430,"navigation":431,"path":432,"seo":433,"stem":434,"__hash__":435},"content\u002Fv1\u002Fcore\u002Fevent-delivery.md","Event delivery",{"type":7,"value":8,"toc":419},"minimark",[9,13,22,40,45,89,96,100,159,163,166,203,214,218,265,275,279,286,290,294,352,356,405,415],[10,11,5],"h1",{"id":12},"event-delivery",[14,15,16,17,21],"p",{},"Async bridges are ",[18,19,20],"strong",{},"eventually consistent",". Core does not pick a queue vendor; it defines behaviours every host must honour.",[14,23,24,25,30,31,30,35,39],{},"Related: ",[26,27,29],"a",{"href":28},"\u002Fv1\u002Fcore\u002Fcross-module-events","Cross-module events"," · ",[26,32,34],{"href":33},"\u002Fv1\u002Fcore\u002Fcross-module-contracts","Contracts",[26,36,38],{"href":37},"\u002Fv1\u002Fcore\u002Ftransactions-and-failures","Transactions",".",[41,42,44],"h2",{"id":43},"terminology","Terminology",[46,47,48,61],"table",{},[49,50,51],"thead",{},[52,53,54,58],"tr",{},[55,56,57],"th",{},"Term",[55,59,60],{},"Meaning",[62,63,64,75],"tbody",{},[52,65,66,72],{},[67,68,69],"td",{},[18,70,71],{},"Sync ACL",[67,73,74],{},"Request\u002Fresponse Anti-Corruption Layer (local port → adapter → peer façade)",[52,76,77,82],{},[67,78,79],{},[18,80,81],{},"Translation listener",[67,83,84,85,88],{},"Consumer ",[18,86,87],{},"Infrastructure"," handler that maps a foreign Domain Event (or webhook) → local Application DTO → inbound Use Case",[14,90,91,92,95],{},"Calling the listener an “ACL listener” is informal shorthand for “translation at the boundary.” Prefer ",[18,93,94],{},"translation listener"," in new docs; both mean Infrastructure-only import of the publisher type.",[41,97,99],{"id":98},"delivery-expectations","Delivery expectations",[46,101,102,112],{},[49,103,104],{},[52,105,106,109],{},[55,107,108],{},"Concern",[55,110,111],{},"Rule",[62,113,114,124,139,149],{},[52,115,116,121],{},[67,117,118],{},[18,119,120],{},"At-least-once",[67,122,123],{},"Assume duplicates; never assume exactly-once from the bus",[52,125,126,131],{},[67,127,128],{},[18,129,130],{},"Idempotency",[67,132,133,134,138],{},"Inbound Use Case (or a dedicated Infra gate before it) must tolerate the same ",[135,136,137],"code",{},"eventId"," twice",[52,140,141,146],{},[67,142,143],{},[18,144,145],{},"Ordering",[67,147,148],{},"Do not assume global order across aggregate types; design consumers for out-of-order when possible",[52,150,151,156],{},[67,152,153],{},[18,154,155],{},"Failure",[67,157,158],{},"Failed handling retries; poison messages need host dead-letter \u002F alert (adapter concern)",[41,160,162],{"id":161},"required-payload-fields-minimum","Required payload fields (minimum)",[14,164,165],{},"On every cross-module Domain Event (or its envelope):",[167,168,169,177,189,197],"ol",{},[170,171,172,176],"li",{},[18,173,174],{},[135,175,137],{}," — unique per occurrence (UUID)",[170,178,179,184,185,188],{},[18,180,181],{},[135,182,183],{},"occurredAt"," — instant the domain fact happened (via Shared ",[135,186,187],{},"ClockInterface"," at publish time)",[170,190,191,196],{},[18,192,193],{},[135,194,195],{},"schemaVersion"," — contract version for this event type",[170,198,199,202],{},[18,200,201],{},"Business payload"," — fields the consumer needs for the happy path (rich enough to avoid Domain re-fetch)",[14,204,205,206,209,210,213],{},"Optional but recommended: ",[135,207,208],{},"correlationId",", ",[135,211,212],{},"causationId",", publisher aggregate id\u002Fcode.",[41,215,217],{"id":216},"idempotency-pattern","Idempotency pattern",[219,220,225],"pre",{"className":221,"code":222,"language":223,"meta":224,"style":224},"language-mermaid shiki shiki-themes github-light github-dark","flowchart TD\n    L[Translation listener] --> M[Map event → local DTO]\n    M --> G{Seen eventId?}\n    G -->|Yes| SKIP[Ack \u002F no-op]\n    G -->|No| UC[Inbound Use Case]\n    UC --> REC[Record processed eventId\u003Cbr\u002F>same transaction as side effect when possible]\n","mermaid","",[135,226,227,235,241,247,253,259],{"__ignoreMap":224},[228,229,232],"span",{"class":230,"line":231},"line",1,[228,233,234],{},"flowchart TD\n",[228,236,238],{"class":230,"line":237},2,[228,239,240],{},"    L[Translation listener] --> M[Map event → local DTO]\n",[228,242,244],{"class":230,"line":243},3,[228,245,246],{},"    M --> G{Seen eventId?}\n",[228,248,250],{"class":230,"line":249},4,[228,251,252],{},"    G -->|Yes| SKIP[Ack \u002F no-op]\n",[228,254,256],{"class":230,"line":255},5,[228,257,258],{},"    G -->|No| UC[Inbound Use Case]\n",[228,260,262],{"class":230,"line":261},6,[228,263,264],{},"    UC --> REC[Record processed eventId\u003Cbr\u002F>same transaction as side effect when possible]\n",[14,266,267,268,270,271,274],{},"Store processed ",[135,269,137],{},"s in the ",[18,272,273],{},"consumer"," module (Infrastructure table or equivalent). Application\u002FDomain stay free of bus APIs.",[41,276,278],{"id":277},"outbox-host-adapter","Outbox (host adapter)",[14,280,281,282,285],{},"Prefer: persist aggregate change + outbox row in ",[18,283,284],{},"one"," publisher transaction; a host worker publishes to the bus afterward. That prevents “Domain committed but event never left.” Concrete outbox tables\u002Fjobs are adapter details.",[287,288],"host-examples",{"topic":289},"async",[41,291,293],{"id":292},"when-a-query-port-is-allowed","When a query port is allowed",[46,295,296,306],{},[49,297,298],{},[52,299,300,303],{},[55,301,302],{},"Pattern",[55,304,305],{},"Allowed?",[62,307,308,318,329,343],{},[52,309,310,313],{},[67,311,312],{},"Happy path: thin event with only an id; listener calls publisher Domain\u002FUse Case",[67,314,315],{},[18,316,317],{},"No",[52,319,320,323],{},[67,321,322],{},"Happy path: rich event → inbound Use Case",[67,324,325,328],{},[18,326,327],{},"Yes"," (default)",[52,330,331,338],{},[67,332,333,334,337],{},"Correction \u002F rebuild \u002F missing optional field: consumer sync port to a ",[18,335,336],{},"thin provider façade"," (HTTP later)",[67,339,340,342],{},[18,341,327],{},", as a separate capability — not a substitute for rich events",[52,344,345,348],{},[67,346,347],{},"Listener imports publisher Application",[67,349,350],{},[18,351,317],{},[41,353,355],{"id":354},"anti-patterns","Anti-patterns",[46,357,358,368],{},[49,359,360],{},[52,361,362,365],{},[55,363,364],{},"Smell",[55,366,367],{},"Fix",[62,369,370,378,389,397],{},[52,371,372,375],{},[67,373,374],{},"“We’ll just query Ordering every time”",[67,376,377],{},"Enrich the event",[52,379,380,383],{},[67,381,382],{},"Idempotency only in the queue framework",[67,384,385,386,388],{},"Persist ",[135,387,137],{}," in the consumer",[52,390,391,394],{},[67,392,393],{},"Swallowing failures silently",[67,395,396],{},"Retry + dead-letter + alert",[52,398,399,402],{},[67,400,401],{},"Putting bus SDKs in Domain",[67,403,404],{},"Shared dispatcher port + Infra adapter",[14,406,407,408,30,412,39],{},"Next: ",[26,409,411],{"href":410},"\u002Fv1\u002Fcore\u002Forchestration","orchestration",[26,413,414],{"href":37},"transactions",[416,417,418],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":224,"searchDepth":237,"depth":237,"links":420},[421,422,423,424,425,426,427],{"id":43,"depth":237,"text":44},{"id":98,"depth":237,"text":99},{"id":161,"depth":237,"text":162},{"id":216,"depth":237,"text":217},{"id":277,"depth":237,"text":278},{"id":292,"depth":237,"text":293},{"id":354,"depth":237,"text":355},"Idempotency, duplicates, correlation, outbox, and when a query port is allowed after Domain Events.","md",{},true,"\u002Fv1\u002Fcore\u002Fevent-delivery",{"title":5,"description":428},"v1\u002Fcore\u002Fevent-delivery","XlGlRtKU_9RTcvdUsuPft-NSgR9tFGSjCHWLOZFhBFo",1786241412123]