[{"data":1,"prerenderedAt":3790},["ShallowReactive",2],{"navigation_docs":3,"-docs-recipes-event-driven-module":493,"-docs-recipes-event-driven-module-surround":3785},[4,46,135,218,257,292,355,377,420],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":45},"Getting Started","i-lucide-rocket","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",[10,15,20,25,30,35,40],{"title":11,"path":12,"stem":13,"icon":14},"What is Heximon?","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F01.getting-started\u002F01.introduction","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F01.getting-started\u002F02.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002F01.getting-started\u002F03.quick-start","i-lucide-zap",{"title":26,"path":27,"stem":28,"icon":29},"Core Concepts","\u002Fdocs\u002Fgetting-started\u002Fcore-concepts","docs\u002F01.getting-started\u002F04.core-concepts","i-lucide-shapes",{"title":31,"path":32,"stem":33,"icon":34},"Examples","\u002Fdocs\u002Fgetting-started\u002Fexamples","docs\u002F01.getting-started\u002F05.examples","i-lucide-library",{"title":36,"path":37,"stem":38,"icon":39},"Where Next?","\u002Fdocs\u002Fgetting-started\u002Fwhere-next","docs\u002F01.getting-started\u002F06.where-next","i-lucide-signpost",{"title":41,"path":42,"stem":43,"icon":44},"Troubleshooting","\u002Fdocs\u002Fgetting-started\u002Ftroubleshooting","docs\u002F01.getting-started\u002F07.troubleshooting","i-lucide-life-buoy",false,{"title":47,"icon":48,"path":49,"stem":50,"children":51,"page":45},"Essentials","i-lucide-boxes","\u002Fdocs\u002Fessentials","docs\u002F02.essentials",[52,56,61,66,71,76,81,86,91,96,101,105,110,115,120,125,130],{"title":53,"path":54,"stem":55,"icon":48},"Modules & DI","\u002Fdocs\u002Fessentials\u002Fmodules-and-di","docs\u002F02.essentials\u002F01.modules-and-di",{"title":57,"path":58,"stem":59,"icon":60},"Controllers","\u002Fdocs\u002Fessentials\u002Fcontrollers","docs\u002F02.essentials\u002F02.controllers","i-lucide-route",{"title":62,"path":63,"stem":64,"icon":65},"Validation & DTOs","\u002Fdocs\u002Fessentials\u002Fvalidation","docs\u002F02.essentials\u002F03.validation","i-lucide-shield-check",{"title":67,"path":68,"stem":69,"icon":70},"Contracts","\u002Fdocs\u002Fessentials\u002Fcontracts","docs\u002F02.essentials\u002F04.contracts","i-lucide-file-signature",{"title":72,"path":73,"stem":74,"icon":75},"REST Client","\u002Fdocs\u002Fessentials\u002Fclient","docs\u002F02.essentials\u002F05.client","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":80},"Database","\u002Fdocs\u002Fessentials\u002Fdatabase","docs\u002F02.essentials\u002F06.database","i-lucide-database",{"title":82,"path":83,"stem":84,"icon":85},"Drizzle ORM","\u002Fdocs\u002Fessentials\u002Fdrizzle","docs\u002F02.essentials\u002F07.drizzle","i-lucide-database-zap",{"title":87,"path":88,"stem":89,"icon":90},"Persistence & Transactions","\u002Fdocs\u002Fessentials\u002Fpersistence","docs\u002F02.essentials\u002F08.persistence","i-lucide-database-backup",{"title":92,"path":93,"stem":94,"icon":95},"Migrations","\u002Fdocs\u002Fessentials\u002Fmigrations","docs\u002F02.essentials\u002F09.migrations","i-lucide-git-branch",{"title":97,"path":98,"stem":99,"icon":100},"Authentication","\u002Fdocs\u002Fessentials\u002Fauthentication","docs\u002F02.essentials\u002F10.authentication","i-lucide-key-round",{"title":102,"path":103,"stem":104,"icon":65},"Permissions & scopes","\u002Fdocs\u002Fessentials\u002Fpermissions","docs\u002F02.essentials\u002F11.permissions",{"title":106,"path":107,"stem":108,"icon":109},"Error Handling","\u002Fdocs\u002Fessentials\u002Ferrors","docs\u002F02.essentials\u002F12.errors","i-lucide-triangle-alert",{"title":111,"path":112,"stem":113,"icon":114},"Testing a Heximon App","\u002Fdocs\u002Fessentials\u002Ftesting","docs\u002F02.essentials\u002F13.testing","i-lucide-flask-conical",{"title":116,"path":117,"stem":118,"icon":119},"Test app harness","\u002Fdocs\u002Fessentials\u002Fcreatetestapp","docs\u002F02.essentials\u002F14.createtestapp","i-lucide-test-tube",{"title":121,"path":122,"stem":123,"icon":124},"Logging & Observability","\u002Fdocs\u002Fessentials\u002Flogging","docs\u002F02.essentials\u002F17.logging","i-lucide-scroll-text",{"title":126,"path":127,"stem":128,"icon":129},"Context & Lifecycle","\u002Fdocs\u002Fessentials\u002Fcontext-and-lifecycle","docs\u002F02.essentials\u002F18.context-and-lifecycle","i-lucide-radio",{"title":131,"path":132,"stem":133,"icon":134},"Project Structure","\u002Fdocs\u002Fessentials\u002Fproject-structure","docs\u002F02.essentials\u002F19.project-structure","i-lucide-folder-tree",{"title":136,"icon":137,"path":138,"stem":139,"children":140,"page":45},"Capabilities","i-lucide-sparkles","\u002Fdocs\u002Fcapabilities","docs\u002F03.capabilities",[141,145,150,155,160,164,169,174,179,184,188,193,198,203,208,213],{"title":142,"path":143,"stem":144,"icon":137},"Overview","\u002Fdocs\u002Fcapabilities\u002Foverview","docs\u002F03.capabilities\u002F01.overview",{"title":146,"path":147,"stem":148,"icon":149},"Events","\u002Fdocs\u002Fcapabilities\u002Fevents","docs\u002F03.capabilities\u002F02.events","i-lucide-radio-tower",{"title":151,"path":152,"stem":153,"icon":154},"Queue","\u002Fdocs\u002Fcapabilities\u002Fbackground-jobs","docs\u002F03.capabilities\u002F03.background-jobs","i-lucide-list-ordered",{"title":156,"path":157,"stem":158,"icon":159},"Scheduled Work","\u002Fdocs\u002Fcapabilities\u002Fscheduled-jobs","docs\u002F03.capabilities\u002F04.scheduled-jobs","i-lucide-clock",{"title":161,"path":162,"stem":163,"icon":85},"Caching","\u002Fdocs\u002Fcapabilities\u002Fcaching","docs\u002F03.capabilities\u002F05.caching",{"title":165,"path":166,"stem":167,"icon":168},"Key\u002FValue Storage","\u002Fdocs\u002Fcapabilities\u002Fkey-value","docs\u002F03.capabilities\u002F06.key-value","i-lucide-hard-drive",{"title":170,"path":171,"stem":172,"icon":173},"Blob Storage","\u002Fdocs\u002Fcapabilities\u002Fblob","docs\u002F03.capabilities\u002F07.blob","i-lucide-folder-open",{"title":175,"path":176,"stem":177,"icon":178},"Health & Readiness","\u002Fdocs\u002Fcapabilities\u002Fhealth","docs\u002F03.capabilities\u002F08.health","i-lucide-heart-pulse",{"title":180,"path":181,"stem":182,"icon":183},"Notifications","\u002Fdocs\u002Fcapabilities\u002Fnotifications","docs\u002F03.capabilities\u002F09.notifications","i-lucide-mail",{"title":185,"path":186,"stem":187,"icon":65},"Idempotency","\u002Fdocs\u002Fcapabilities\u002Fidempotency","docs\u002F03.capabilities\u002F10.idempotency",{"title":189,"path":190,"stem":191,"icon":192},"Security Hardening","\u002Fdocs\u002Fcapabilities\u002Fsecurity","docs\u002F03.capabilities\u002F11.security","i-lucide-shield",{"title":194,"path":195,"stem":196,"icon":197},"Observability & Tracing","\u002Fdocs\u002Fcapabilities\u002Fobservability","docs\u002F03.capabilities\u002F12.observability","i-lucide-activity",{"title":199,"path":200,"stem":201,"icon":202},"OpenAPI","\u002Fdocs\u002Fcapabilities\u002Fopenapi","docs\u002F03.capabilities\u002F13.openapi","i-lucide-book-marked",{"title":204,"path":205,"stem":206,"icon":207},"MCP Server","\u002Fdocs\u002Fcapabilities\u002Fmcp","docs\u002F03.capabilities\u002F14.mcp","i-lucide-bot",{"title":209,"path":210,"stem":211,"icon":212},"Schema DTOs","\u002Fdocs\u002Fcapabilities\u002Fschema","docs\u002F03.capabilities\u002F15.schema","i-lucide-file-json",{"title":214,"path":215,"stem":216,"icon":217},"Branded IDs","\u002Fdocs\u002Fcapabilities\u002Fbranded-ids","docs\u002F03.capabilities\u002F16.branded-ids","i-lucide-tag",{"title":219,"icon":220,"path":221,"stem":222,"children":223,"page":45},"Architecture","i-lucide-building","\u002Fdocs\u002Farchitecture","docs\u002F04.architecture",[224,227,232,237,242,247,252],{"title":142,"path":225,"stem":226,"icon":220},"\u002Fdocs\u002Farchitecture\u002Foverview","docs\u002F04.architecture\u002F01.overview",{"title":228,"path":229,"stem":230,"icon":231},"Domain-Driven Design","\u002Fdocs\u002Farchitecture\u002Fddd","docs\u002F04.architecture\u002F02.ddd","i-lucide-hexagon",{"title":233,"path":234,"stem":235,"icon":236},"CQRS","\u002Fdocs\u002Farchitecture\u002Fcqrs","docs\u002F04.architecture\u002F03.cqrs","i-lucide-split",{"title":238,"path":239,"stem":240,"icon":241},"Repository Pattern","\u002Fdocs\u002Farchitecture\u002Frepository-pattern","docs\u002F04.architecture\u002F04.repository-pattern","i-lucide-archive",{"title":243,"path":244,"stem":245,"icon":246},"Integration Events","\u002Fdocs\u002Farchitecture\u002Fintegration-events","docs\u002F04.architecture\u002F05.integration-events","i-lucide-send-horizontal",{"title":248,"path":249,"stem":250,"icon":251},"Saga Orchestration","\u002Fdocs\u002Farchitecture\u002Fsagas","docs\u002F04.architecture\u002F06.sagas","i-lucide-hourglass",{"title":253,"path":254,"stem":255,"icon":256},"Durable Workflows","\u002Fdocs\u002Farchitecture\u002Fworkflows","docs\u002F04.architecture\u002F07.workflows","i-lucide-workflow",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":45},"Realtime","i-lucide-cable","\u002Fdocs\u002Frealtime","docs\u002F05.realtime",[263,266,270,274,279,284,288],{"title":142,"path":264,"stem":265,"icon":259},"\u002Fdocs\u002Frealtime\u002Foverview","docs\u002F05.realtime\u002F01.overview",{"title":267,"path":268,"stem":269,"icon":129},"WebSockets","\u002Fdocs\u002Frealtime\u002Fwebsockets","docs\u002F05.realtime\u002F02.websockets",{"title":271,"path":272,"stem":273,"icon":70},"WebSocket Contracts","\u002Fdocs\u002Frealtime\u002Fwebsocket-contracts","docs\u002F05.realtime\u002F03.websocket-contracts",{"title":275,"path":276,"stem":277,"icon":278},"Server-Sent Events","\u002Fdocs\u002Frealtime\u002Fserver-sent-events","docs\u002F05.realtime\u002F04.server-sent-events","i-lucide-rss",{"title":280,"path":281,"stem":282,"icon":283},"Typed Clients","\u002Fdocs\u002Frealtime\u002Ftyped-clients","docs\u002F05.realtime\u002F05.typed-clients","i-lucide-plug-zap",{"title":285,"path":286,"stem":287,"icon":259},"AsyncAPI","\u002Fdocs\u002Frealtime\u002Fasyncapi","docs\u002F05.realtime\u002F06.asyncapi",{"title":289,"path":290,"stem":291,"icon":80},"Durable Objects","\u002Fdocs\u002Frealtime\u002Fdurable-objects","docs\u002F05.realtime\u002F07.durable-objects",{"title":293,"icon":294,"path":295,"stem":296,"children":297,"page":45},"Deploy","i-lucide-cloud-upload","\u002Fdocs\u002Fdeploy","docs\u002F06.deploy",[298,301,306,311,316,321,326,331,336,341,345,350],{"title":142,"path":299,"stem":300,"icon":6},"\u002Fdocs\u002Fdeploy\u002Foverview","docs\u002F06.deploy\u002F01.overview",{"title":302,"path":303,"stem":304,"icon":305},"Node","\u002Fdocs\u002Fdeploy\u002Fnode","docs\u002F06.deploy\u002F02.node","la:node-js",{"title":307,"path":308,"stem":309,"icon":310},"Cloudflare","\u002Fdocs\u002Fdeploy\u002Fcloudflare","docs\u002F06.deploy\u002F03.cloudflare","simple-icons:cloudflare",{"title":312,"path":313,"stem":314,"icon":315},"Vercel","\u002Fdocs\u002Fdeploy\u002Fvercel","docs\u002F06.deploy\u002F04.vercel","ri:vercel-fill",{"title":317,"path":318,"stem":319,"icon":320},"Netlify","\u002Fdocs\u002Fdeploy\u002Fnetlify","docs\u002F06.deploy\u002F05.netlify","i-simple-icons-netlify",{"title":322,"path":323,"stem":324,"icon":325},"AWS Lambda","\u002Fdocs\u002Fdeploy\u002Faws-lambda","docs\u002F06.deploy\u002F06.aws-lambda","hugeicons:aws-lambda",{"title":327,"path":328,"stem":329,"icon":330},"Deno Deploy","\u002Fdocs\u002Fdeploy\u002Fdeno-deploy","docs\u002F06.deploy\u002F07.deno-deploy","simple-icons:denodeploy",{"title":332,"path":333,"stem":334,"icon":335},"Nitro","\u002Fdocs\u002Fdeploy\u002Fnitro","docs\u002F06.deploy\u002F08.nitro","i-unjs-nitro",{"title":337,"path":338,"stem":339,"icon":340},"Nuxt","\u002Fdocs\u002Fdeploy\u002Fnuxt","docs\u002F06.deploy\u002F09.nuxt","i-devicon-nuxt",{"title":342,"path":343,"stem":344,"icon":236},"Mixed-runtime split","\u002Fdocs\u002Fdeploy\u002Fmixed-runtime-split","docs\u002F06.deploy\u002F10.mixed-runtime-split",{"title":346,"path":347,"stem":348,"icon":349},"Hosts","\u002Fdocs\u002Fdeploy\u002Fhosts","docs\u002F06.deploy\u002F11.hosts","i-lucide-map",{"title":351,"path":352,"stem":353,"icon":354},"Cross-service events","\u002Fdocs\u002Fdeploy\u002Fcross-service-events","docs\u002F06.deploy\u002F12.cross-service-events","i-lucide-network",{"title":356,"icon":357,"path":358,"stem":359,"children":360,"page":45},"CLI","i-lucide-terminal","\u002Fdocs\u002Fcli","docs\u002F07.cli",[361,365,369,373],{"title":362,"path":363,"stem":364,"icon":357},"The heximon CLI","\u002Fdocs\u002Fcli\u002Foverview","docs\u002F07.cli\u002F01.overview",{"title":366,"path":367,"stem":368,"icon":137},"Create a New App","\u002Fdocs\u002Fcli\u002Fcreate","docs\u002F07.cli\u002F02.create",{"title":370,"path":371,"stem":372,"icon":95},"Migrations from the CLI","\u002Fdocs\u002Fcli\u002Fmigrations","docs\u002F07.cli\u002F03.migrations",{"title":374,"path":375,"stem":376,"icon":6},"Deploying a Heximon app","\u002Fdocs\u002Fcli\u002Fdeploy","docs\u002F07.cli\u002F04.deploy",{"title":378,"icon":379,"path":380,"stem":381,"children":382,"page":45},"Under the Hood","i-lucide-wrench","\u002Fdocs\u002Funder-the-hood","docs\u002F08.under-the-hood",[383,388,393,397,402,407,411,415],{"title":384,"path":385,"stem":386,"icon":387},"How the Compiler Works","\u002Fdocs\u002Funder-the-hood\u002Fhow-the-compiler-works","docs\u002F08.under-the-hood\u002F01.how-the-compiler-works","i-lucide-cog",{"title":389,"path":390,"stem":391,"icon":392},"Compiler","\u002Fdocs\u002Funder-the-hood\u002Fextending-the-compiler","docs\u002F08.under-the-hood\u002F02.extending-the-compiler","i-lucide-cpu",{"title":394,"path":395,"stem":396,"icon":357},"Compiler Diagnostics","\u002Fdocs\u002Funder-the-hood\u002Fcompiler-diagnostics","docs\u002F08.under-the-hood\u002F03.compiler-diagnostics",{"title":398,"path":399,"stem":400,"icon":401},"Library Mode","\u002Fdocs\u002Funder-the-hood\u002Flibrary-mode","docs\u002F08.under-the-hood\u002F04.library-mode","i-lucide-package",{"title":403,"path":404,"stem":405,"icon":406},"Other bundlers","\u002Fdocs\u002Funder-the-hood\u002Falternate-bundlers","docs\u002F08.under-the-hood\u002F05.alternate-bundlers","i-lucide-blocks",{"title":408,"path":409,"stem":410,"icon":401},"Packages","\u002Fdocs\u002Funder-the-hood\u002Fpackages","docs\u002F08.under-the-hood\u002F06.packages",{"title":412,"path":413,"stem":414,"icon":207},"AI Skills","\u002Fdocs\u002Funder-the-hood\u002Fai-and-agents","docs\u002F08.under-the-hood\u002F07.ai-and-agents",{"title":416,"path":417,"stem":418,"icon":419},"Advanced DI","\u002Fdocs\u002Funder-the-hood\u002Fadvanced-di","docs\u002F08.under-the-hood\u002F08.advanced-di","i-lucide-git-fork",{"title":421,"icon":422,"path":423,"stem":424,"children":425,"page":45},"Recipes","i-lucide-book-open-check","\u002Fdocs\u002Frecipes","docs\u002F09.recipes",[426,431,436,440,444,448,452,456,460,465,469,474,479,484,488],{"title":427,"path":428,"stem":429,"icon":430},"Send an Email","\u002Fdocs\u002Frecipes\u002Fsend-an-email","docs\u002F09.recipes\u002F01.send-an-email","i-lucide-send",{"title":432,"path":433,"stem":434,"icon":435},"Upload a File","\u002Fdocs\u002Frecipes\u002Fupload-a-file","docs\u002F09.recipes\u002F02.upload-a-file","i-lucide-upload",{"title":437,"path":438,"stem":439,"icon":24},"Cache an Expensive Call","\u002Fdocs\u002Frecipes\u002Fcache-an-expensive-call","docs\u002F09.recipes\u002F03.cache-an-expensive-call",{"title":441,"path":442,"stem":443,"icon":154},"Paginate a List","\u002Fdocs\u002Frecipes\u002Fpaginate-a-list","docs\u002F09.recipes\u002F04.paginate-a-list",{"title":445,"path":446,"stem":447,"icon":65},"Protect Admin Routes","\u002Fdocs\u002Frecipes\u002Fprotect-admin-routes","docs\u002F09.recipes\u002F05.protect-admin-routes",{"title":449,"path":450,"stem":451,"icon":178},"Health Probes and a Nightly Job","\u002Fdocs\u002Frecipes\u002Fhealth-probes-and-a-nightly-job","docs\u002F09.recipes\u002F06.health-probes-and-a-nightly-job",{"title":453,"path":454,"stem":455,"icon":80},"Build a CRUD API","\u002Fdocs\u002Frecipes\u002Fcrud-api","docs\u002F09.recipes\u002F07.crud-api",{"title":457,"path":458,"stem":459,"icon":100},"Add Authentication","\u002Fdocs\u002Frecipes\u002Fadd-authentication","docs\u002F09.recipes\u002F08.add-authentication",{"title":461,"path":462,"stem":463,"icon":464},"Background Jobs","\u002Fdocs\u002Frecipes\u002Fbackground-jobs","docs\u002F09.recipes\u002F09.background-jobs","i-lucide-timer",{"title":466,"path":467,"stem":468,"icon":149},"Decouple Features with Events","\u002Fdocs\u002Frecipes\u002Fevent-driven-module","docs\u002F09.recipes\u002F10.event-driven-module",{"title":470,"path":471,"stem":472,"icon":473},"Build a DDD Aggregate","\u002Fdocs\u002Frecipes\u002Fddd-aggregate","docs\u002F09.recipes\u002F11.ddd-aggregate","i-lucide-box",{"title":475,"path":476,"stem":477,"icon":478},"Publish a Feature Package","\u002Fdocs\u002Frecipes\u002Ffeature-package","docs\u002F09.recipes\u002F12.feature-package","i-lucide-package-open",{"title":480,"path":481,"stem":482,"icon":483},"Reliable Integration Events","\u002Fdocs\u002Frecipes\u002Freliable-integration-events","docs\u002F09.recipes\u002F13.reliable-integration-events","i-lucide-inbox",{"title":485,"path":486,"stem":487,"icon":95},"Reactive Choreography","\u002Fdocs\u002Frecipes\u002Freactive-choreography","docs\u002F09.recipes\u002F14.reactive-choreography",{"title":489,"path":490,"stem":491,"icon":492},"Build a Production CRUD API","\u002Fdocs\u002Frecipes\u002Fproduction-crud-api","docs\u002F09.recipes\u002F15.production-crud-api","i-lucide-layers",{"id":494,"title":466,"body":495,"description":3778,"extension":3779,"links":3780,"meta":3781,"navigation":3782,"path":467,"seo":3783,"stem":468,"__hash__":3784},"docs\u002Fdocs\u002F09.recipes\u002F10.event-driven-module.md",{"type":496,"value":497,"toc":3768},"minimark",[498,502,505,510,526,639,642,646,664,679,992,996,1015,1022,1274,1289,1565,1572,1576,1597,2013,2024,2028,2053,2242,2245,2341,2348,2462,2466,2472,2596,2602,2684,2687,2754,2758,2772,2783,2786,2906,2917,3309,3316,3448,3459,3684,3705,3711,3715,3764],[499,500,501],"p",{},"You want one feature to react to another without the two knowing about each other: placing an order\nshould send a confirmation email and record a metric, but the order code shouldn't import any of that.",[499,503,504],{},"Events give you that seam — a publisher emits, subscribers react, and the only thing they share is the\nevent's shape. Here you'll build a shop module that emits an order event and a separate notifications\nmodule that reacts to it, so that removing the subscriber is a legitimate no-op, not a compile error.",[506,507,509],"h2",{"id":508},"pick-the-right-tier","Pick the right tier",[499,511,512,513,517,518,521,522,525],{},"Heximon's three event tiers differ in ",[514,515,516],"em",{},"scope"," (who can react), ",[514,519,520],{},"delivery"," (sync vs. queued), and ",[514,523,524],{},"failure\npolicy"," (does a failed reaction roll back the originating work). Get the choice wrong and a metrics outage\nrolls back a paid order, or a critical email silently disappears.",[527,528,529,549],"table",{},[530,531,532],"thead",{},[533,534,535,540,543,546],"tr",{},[536,537,539],"th",{"align":538},"left","Tier",[536,541,542],{"align":538},"Bind a handler with",[536,544,545],{"align":538},"Scope & delivery",[536,547,548],{"align":538},"Pick it when…",[550,551,552,577,599,621],"tbody",{},[533,553,554,561,567,570],{},[555,556,557],"td",{"align":538},[558,559,560],"strong",{},"In-process (serial)",[555,562,563],{"align":538},[564,565,566],"code",{},"implements EventHandler\u003CSomeEvent>",[555,568,569],{"align":538},"same process, in the request",[555,571,572,573,576],{"align":538},"a handler ",[558,574,575],{},"must"," succeed for the originating work to count — its throw should roll the operation back",[533,578,579,584,589,592],{},[555,580,581],{"align":538},[558,582,583],{},"In-process (notification)",[555,585,586],{"align":538},[564,587,588],{},"implements NotificationHandler\u003CSomeEvent>",[555,590,591],{"align":538},"same process, after the serial bucket settles",[555,593,594,595,598],{"align":538},"the reaction is a side effect (metrics, projections) that must ",[558,596,597],{},"never"," roll back the originating work",[533,600,601,606,611,614],{},[555,602,603],{"align":538},[558,604,605],{},"Domain",[555,607,608],{"align":538},[564,609,610],{},"implements DomainEventHandler\u003CSomeEvent>",[555,612,613],{"align":538},"same process, flushed after a save",[555,615,616,617,620],{"align":538},"the event is recorded ",[514,618,619],{},"on an aggregate"," and should fire only once the write commits",[533,622,623,628,633,636],{},[555,624,625],{"align":538},[558,626,627],{},"Integration",[555,629,630],{"align":538},[564,631,632],{},"implements IntegrationEventHandler\u003C\"type\", Payload>",[555,634,635],{"align":538},"another context, drained from a queue",[555,637,638],{"align":538},"the reaction crosses a service boundary, or must survive the request that triggered it",[499,640,641],{},"Stay in-process until the work must outlive the request — then reach for the integration tier and a queue.\nThis recipe builds the in-process tier end to end, then shows where the domain and integration tiers take\nover.",[506,643,645],{"id":644},"_1-declare-the-event","1. Declare the event",[499,647,648,649,652,653,656,657,659,660,663],{},"An in-process event extends ",[564,650,651],{},"BaseEvent"," — its ",[558,654,655],{},"class identity"," is what handlers bind to, so there are no\nstring keys to keep in sync, and the constructor parameters are the typed payload. Extending ",[564,658,651],{},"\nis what lets the publisher ",[564,661,662],{},"emit(new OrderPlacedEvent(...))"," with no cast.",[499,665,666,667,670,671,673,674,678],{},"(A DDD app extends ",[564,668,669],{},"DomainEvent"," instead, which itself extends ",[564,672,651],{},", to record the event on an\naggregate — see ",[675,676,677],"a",{"href":471},"Model a DDD aggregate",".)",[680,681,687],"pre",{"className":682,"code":683,"filename":684,"language":685,"meta":686,"style":686},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { BaseEvent } from \"@heximon\u002Fevents\";\n\nexport interface OrderLineItem {\n  readonly productId: string;\n  readonly quantity: number;\n}\n\nexport class OrderPlacedEvent extends BaseEvent {\n  public constructor(\n    public readonly orderId: string,\n    public readonly customerEmail: string,\n    public readonly lineItems: readonly OrderLineItem[],\n  ) {\n    super();\n  }\n\n  public describe(): string {\n    return `order ${this.orderId} (${this.lineItems.length} item(s)) for ${this.customerEmail}`;\n  }\n}\n","src\u002Fshop\u002Forder-events.ts","ts","",[564,688,689,725,732,749,767,782,788,793,811,823,843,859,880,888,899,905,910,925,982,987],{"__ignoreMap":686},[690,691,694,698,702,706,709,712,715,719,722],"span",{"class":692,"line":693},"line",1,[690,695,697],{"class":696},"s7zQu","import",[690,699,701],{"class":700},"sMK4o"," {",[690,703,705],{"class":704},"sTEyZ"," BaseEvent",[690,707,708],{"class":700}," }",[690,710,711],{"class":696}," from",[690,713,714],{"class":700}," \"",[690,716,718],{"class":717},"sfazB","@heximon\u002Fevents",[690,720,721],{"class":700},"\"",[690,723,724],{"class":700},";\n",[690,726,728],{"class":692,"line":727},2,[690,729,731],{"emptyLinePlaceholder":730},true,"\n",[690,733,735,738,742,746],{"class":692,"line":734},3,[690,736,737],{"class":696},"export",[690,739,741],{"class":740},"spNyl"," interface",[690,743,745],{"class":744},"sBMFI"," OrderLineItem",[690,747,748],{"class":700}," {\n",[690,750,752,755,759,762,765],{"class":692,"line":751},4,[690,753,754],{"class":740},"  readonly",[690,756,758],{"class":757},"swJcz"," productId",[690,760,761],{"class":700},":",[690,763,764],{"class":744}," string",[690,766,724],{"class":700},[690,768,770,772,775,777,780],{"class":692,"line":769},5,[690,771,754],{"class":740},[690,773,774],{"class":757}," quantity",[690,776,761],{"class":700},[690,778,779],{"class":744}," number",[690,781,724],{"class":700},[690,783,785],{"class":692,"line":784},6,[690,786,787],{"class":700},"}\n",[690,789,791],{"class":692,"line":790},7,[690,792,731],{"emptyLinePlaceholder":730},[690,794,796,798,801,804,807,809],{"class":692,"line":795},8,[690,797,737],{"class":696},[690,799,800],{"class":740}," class",[690,802,803],{"class":744}," OrderPlacedEvent",[690,805,806],{"class":740}," extends",[690,808,705],{"class":744},[690,810,748],{"class":700},[690,812,814,817,820],{"class":692,"line":813},9,[690,815,816],{"class":740},"  public",[690,818,819],{"class":740}," constructor",[690,821,822],{"class":700},"(\n",[690,824,826,829,832,836,838,840],{"class":692,"line":825},10,[690,827,828],{"class":740},"    public",[690,830,831],{"class":740}," readonly",[690,833,835],{"class":834},"sHdIc"," orderId",[690,837,761],{"class":700},[690,839,764],{"class":744},[690,841,842],{"class":700},",\n",[690,844,846,848,850,853,855,857],{"class":692,"line":845},11,[690,847,828],{"class":740},[690,849,831],{"class":740},[690,851,852],{"class":834}," customerEmail",[690,854,761],{"class":700},[690,856,764],{"class":744},[690,858,842],{"class":700},[690,860,862,864,866,869,871,873,875,878],{"class":692,"line":861},12,[690,863,828],{"class":740},[690,865,831],{"class":740},[690,867,868],{"class":834}," lineItems",[690,870,761],{"class":700},[690,872,831],{"class":740},[690,874,745],{"class":744},[690,876,877],{"class":704},"[]",[690,879,842],{"class":700},[690,881,883,886],{"class":692,"line":882},13,[690,884,885],{"class":700},"  )",[690,887,748],{"class":700},[690,889,891,894,897],{"class":692,"line":890},14,[690,892,893],{"class":704},"    super",[690,895,896],{"class":757},"()",[690,898,724],{"class":700},[690,900,902],{"class":692,"line":901},15,[690,903,904],{"class":700},"  }\n",[690,906,908],{"class":692,"line":907},16,[690,909,731],{"emptyLinePlaceholder":730},[690,911,913,915,918,921,923],{"class":692,"line":912},17,[690,914,816],{"class":740},[690,916,917],{"class":757}," describe",[690,919,920],{"class":700},"():",[690,922,764],{"class":744},[690,924,748],{"class":700},[690,926,928,931,934,937,940,943,946,949,952,954,956,959,962,965,967,970,972,974,977,980],{"class":692,"line":927},18,[690,929,930],{"class":696},"    return",[690,932,933],{"class":700}," `",[690,935,936],{"class":717},"order ",[690,938,939],{"class":700},"${",[690,941,942],{"class":700},"this.",[690,944,945],{"class":704},"orderId",[690,947,948],{"class":700},"}",[690,950,951],{"class":717}," (",[690,953,939],{"class":700},[690,955,942],{"class":700},[690,957,958],{"class":704},"lineItems",[690,960,961],{"class":700},".",[690,963,964],{"class":704},"length",[690,966,948],{"class":700},[690,968,969],{"class":717}," item(s)) for ",[690,971,939],{"class":700},[690,973,942],{"class":700},[690,975,976],{"class":704},"customerEmail",[690,978,979],{"class":700},"}`",[690,981,724],{"class":700},[690,983,985],{"class":692,"line":984},19,[690,986,904],{"class":700},[690,988,990],{"class":692,"line":989},20,[690,991,787],{"class":700},[506,993,995],{"id":994},"_2-write-the-handlers","2. Write the handlers",[499,997,998,999,1002,1003,1006,1007,1010,1011,1014],{},"A handler binds to the event by ",[564,1000,1001],{},"implements","ing one of two bases, both declaring ",[564,1004,1005],{},"handle(event)",". The event\nclass is the ",[558,1008,1009],{},"type argument"," — ",[564,1012,1013],{},"implements EventHandler\u003COrderPlacedEvent>"," — which the compiler reads from\nthe type position, so the class is type-only here.",[499,1016,1017,1018,1021],{},"A serial ",[564,1019,1020],{},"EventHandler"," runs in declaration order, and a throw propagates back to the publisher — what you\nwant when the confirmation email is part of the order succeeding:",[680,1023,1026],{"className":682,"code":1024,"filename":1025,"language":685,"meta":686,"style":686},"import type { EventHandler } from \"@heximon\u002Fevents\";\nimport { OrderPlacedEvent } from \"..\u002Fshop\u002Forder-events\"; \u002F\u002F the bound event class is the type argument\nimport { EventLog } from \".\u002Fevent-log\";\n\nexport class ConfirmationEmailHandler implements EventHandler\u003COrderPlacedEvent> {\n  public constructor(private readonly eventLog: EventLog) {}\n\n  public handle(event: OrderPlacedEvent): void {\n    this.eventLog.record(\n      \"confirmation-email\",\n      event.orderId,\n      `Confirmation email sent to ${event.customerEmail} for ${event.describe()}`,\n    );\n  }\n}\n","src\u002Fnotifications\u002Fconfirmation-email.handler.ts",[564,1027,1028,1052,1078,1100,1104,1129,1156,1160,1184,1200,1212,1223,1259,1266,1270],{"__ignoreMap":686},[690,1029,1030,1032,1035,1037,1040,1042,1044,1046,1048,1050],{"class":692,"line":693},[690,1031,697],{"class":696},[690,1033,1034],{"class":696}," type",[690,1036,701],{"class":700},[690,1038,1039],{"class":704}," EventHandler",[690,1041,708],{"class":700},[690,1043,711],{"class":696},[690,1045,714],{"class":700},[690,1047,718],{"class":717},[690,1049,721],{"class":700},[690,1051,724],{"class":700},[690,1053,1054,1056,1058,1060,1062,1064,1066,1069,1071,1074],{"class":692,"line":727},[690,1055,697],{"class":696},[690,1057,701],{"class":700},[690,1059,803],{"class":704},[690,1061,708],{"class":700},[690,1063,711],{"class":696},[690,1065,714],{"class":700},[690,1067,1068],{"class":717},"..\u002Fshop\u002Forder-events",[690,1070,721],{"class":700},[690,1072,1073],{"class":700},";",[690,1075,1077],{"class":1076},"sHwdD"," \u002F\u002F the bound event class is the type argument\n",[690,1079,1080,1082,1084,1087,1089,1091,1093,1096,1098],{"class":692,"line":734},[690,1081,697],{"class":696},[690,1083,701],{"class":700},[690,1085,1086],{"class":704}," EventLog",[690,1088,708],{"class":700},[690,1090,711],{"class":696},[690,1092,714],{"class":700},[690,1094,1095],{"class":717},".\u002Fevent-log",[690,1097,721],{"class":700},[690,1099,724],{"class":700},[690,1101,1102],{"class":692,"line":751},[690,1103,731],{"emptyLinePlaceholder":730},[690,1105,1106,1108,1110,1113,1116,1118,1121,1124,1127],{"class":692,"line":769},[690,1107,737],{"class":696},[690,1109,800],{"class":740},[690,1111,1112],{"class":744}," ConfirmationEmailHandler",[690,1114,1115],{"class":740}," implements",[690,1117,1039],{"class":744},[690,1119,1120],{"class":700},"\u003C",[690,1122,1123],{"class":744},"OrderPlacedEvent",[690,1125,1126],{"class":700},">",[690,1128,748],{"class":700},[690,1130,1131,1133,1135,1138,1141,1143,1146,1148,1150,1153],{"class":692,"line":784},[690,1132,816],{"class":740},[690,1134,819],{"class":740},[690,1136,1137],{"class":700},"(",[690,1139,1140],{"class":740},"private",[690,1142,831],{"class":740},[690,1144,1145],{"class":834}," eventLog",[690,1147,761],{"class":700},[690,1149,1086],{"class":744},[690,1151,1152],{"class":700},")",[690,1154,1155],{"class":700}," {}\n",[690,1157,1158],{"class":692,"line":790},[690,1159,731],{"emptyLinePlaceholder":730},[690,1161,1162,1164,1167,1169,1172,1174,1176,1179,1182],{"class":692,"line":795},[690,1163,816],{"class":740},[690,1165,1166],{"class":757}," handle",[690,1168,1137],{"class":700},[690,1170,1171],{"class":834},"event",[690,1173,761],{"class":700},[690,1175,803],{"class":744},[690,1177,1178],{"class":700},"):",[690,1180,1181],{"class":744}," void",[690,1183,748],{"class":700},[690,1185,1186,1189,1192,1194,1198],{"class":692,"line":813},[690,1187,1188],{"class":700},"    this.",[690,1190,1191],{"class":704},"eventLog",[690,1193,961],{"class":700},[690,1195,1197],{"class":1196},"s2Zo4","record",[690,1199,822],{"class":757},[690,1201,1202,1205,1208,1210],{"class":692,"line":825},[690,1203,1204],{"class":700},"      \"",[690,1206,1207],{"class":717},"confirmation-email",[690,1209,721],{"class":700},[690,1211,842],{"class":700},[690,1213,1214,1217,1219,1221],{"class":692,"line":845},[690,1215,1216],{"class":704},"      event",[690,1218,961],{"class":700},[690,1220,945],{"class":704},[690,1222,842],{"class":700},[690,1224,1225,1228,1231,1233,1235,1237,1239,1241,1244,1246,1248,1250,1253,1255,1257],{"class":692,"line":861},[690,1226,1227],{"class":700},"      `",[690,1229,1230],{"class":717},"Confirmation email sent to ",[690,1232,939],{"class":700},[690,1234,1171],{"class":704},[690,1236,961],{"class":700},[690,1238,976],{"class":704},[690,1240,948],{"class":700},[690,1242,1243],{"class":717}," for ",[690,1245,939],{"class":700},[690,1247,1171],{"class":704},[690,1249,961],{"class":700},[690,1251,1252],{"class":1196},"describe",[690,1254,896],{"class":704},[690,1256,979],{"class":700},[690,1258,842],{"class":700},[690,1260,1261,1264],{"class":692,"line":882},[690,1262,1263],{"class":757},"    )",[690,1265,724],{"class":700},[690,1267,1268],{"class":692,"line":890},[690,1269,904],{"class":700},[690,1271,1272],{"class":692,"line":901},[690,1273,787],{"class":700},[499,1275,1276,1277,1280,1281,1284,1285,1288],{},"A ",[564,1278,1279],{},"NotificationHandler"," runs ",[514,1282,1283],{},"concurrently"," after the serial bucket settles, and its failure is\n",[558,1286,1287],{},"absorbed"," — so a metrics outage can never undo a placed order:",[680,1290,1293],{"className":682,"code":1291,"filename":1292,"language":685,"meta":686,"style":686},"import type { NotificationHandler } from \"@heximon\u002Fevents\";\nimport type { OrderPlacedEvent } from \"..\u002Fshop\u002Forder-events\";\nimport { EventLog } from \".\u002Fevent-log\";\n\nexport class OrderMetricsHandler implements NotificationHandler\u003COrderPlacedEvent> {\n  public constructor(private readonly eventLog: EventLog) {}\n\n  public async handle(event: OrderPlacedEvent): Promise\u003Cvoid> {\n    const totalUnits = event.lineItems.reduce((sum, item) => sum + item.quantity, 0);\n    this.eventLog.record(\"order-metrics\", event.orderId, `Recorded ${totalUnits} unit(s)`);\n  }\n}\n","src\u002Fnotifications\u002Forder-metrics.handler.ts",[564,1294,1295,1318,1340,1360,1364,1385,1407,1411,1442,1506,1557,1561],{"__ignoreMap":686},[690,1296,1297,1299,1301,1303,1306,1308,1310,1312,1314,1316],{"class":692,"line":693},[690,1298,697],{"class":696},[690,1300,1034],{"class":696},[690,1302,701],{"class":700},[690,1304,1305],{"class":704}," NotificationHandler",[690,1307,708],{"class":700},[690,1309,711],{"class":696},[690,1311,714],{"class":700},[690,1313,718],{"class":717},[690,1315,721],{"class":700},[690,1317,724],{"class":700},[690,1319,1320,1322,1324,1326,1328,1330,1332,1334,1336,1338],{"class":692,"line":727},[690,1321,697],{"class":696},[690,1323,1034],{"class":696},[690,1325,701],{"class":700},[690,1327,803],{"class":704},[690,1329,708],{"class":700},[690,1331,711],{"class":696},[690,1333,714],{"class":700},[690,1335,1068],{"class":717},[690,1337,721],{"class":700},[690,1339,724],{"class":700},[690,1341,1342,1344,1346,1348,1350,1352,1354,1356,1358],{"class":692,"line":734},[690,1343,697],{"class":696},[690,1345,701],{"class":700},[690,1347,1086],{"class":704},[690,1349,708],{"class":700},[690,1351,711],{"class":696},[690,1353,714],{"class":700},[690,1355,1095],{"class":717},[690,1357,721],{"class":700},[690,1359,724],{"class":700},[690,1361,1362],{"class":692,"line":751},[690,1363,731],{"emptyLinePlaceholder":730},[690,1365,1366,1368,1370,1373,1375,1377,1379,1381,1383],{"class":692,"line":769},[690,1367,737],{"class":696},[690,1369,800],{"class":740},[690,1371,1372],{"class":744}," OrderMetricsHandler",[690,1374,1115],{"class":740},[690,1376,1305],{"class":744},[690,1378,1120],{"class":700},[690,1380,1123],{"class":744},[690,1382,1126],{"class":700},[690,1384,748],{"class":700},[690,1386,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405],{"class":692,"line":784},[690,1388,816],{"class":740},[690,1390,819],{"class":740},[690,1392,1137],{"class":700},[690,1394,1140],{"class":740},[690,1396,831],{"class":740},[690,1398,1145],{"class":834},[690,1400,761],{"class":700},[690,1402,1086],{"class":744},[690,1404,1152],{"class":700},[690,1406,1155],{"class":700},[690,1408,1409],{"class":692,"line":790},[690,1410,731],{"emptyLinePlaceholder":730},[690,1412,1413,1415,1418,1420,1422,1424,1426,1428,1430,1433,1435,1438,1440],{"class":692,"line":795},[690,1414,816],{"class":740},[690,1416,1417],{"class":740}," async",[690,1419,1166],{"class":757},[690,1421,1137],{"class":700},[690,1423,1171],{"class":834},[690,1425,761],{"class":700},[690,1427,803],{"class":744},[690,1429,1178],{"class":700},[690,1431,1432],{"class":744}," Promise",[690,1434,1120],{"class":700},[690,1436,1437],{"class":744},"void",[690,1439,1126],{"class":700},[690,1441,748],{"class":700},[690,1443,1444,1447,1450,1453,1456,1458,1460,1462,1465,1467,1469,1472,1475,1478,1480,1483,1486,1489,1491,1493,1496,1498,1502,1504],{"class":692,"line":813},[690,1445,1446],{"class":740},"    const",[690,1448,1449],{"class":704}," totalUnits",[690,1451,1452],{"class":700}," =",[690,1454,1455],{"class":704}," event",[690,1457,961],{"class":700},[690,1459,958],{"class":704},[690,1461,961],{"class":700},[690,1463,1464],{"class":1196},"reduce",[690,1466,1137],{"class":757},[690,1468,1137],{"class":700},[690,1470,1471],{"class":834},"sum",[690,1473,1474],{"class":700},",",[690,1476,1477],{"class":834}," item",[690,1479,1152],{"class":700},[690,1481,1482],{"class":740}," =>",[690,1484,1485],{"class":704}," sum",[690,1487,1488],{"class":700}," +",[690,1490,1477],{"class":704},[690,1492,961],{"class":700},[690,1494,1495],{"class":704},"quantity",[690,1497,1474],{"class":700},[690,1499,1501],{"class":1500},"sbssI"," 0",[690,1503,1152],{"class":757},[690,1505,724],{"class":700},[690,1507,1508,1510,1512,1514,1516,1518,1520,1523,1525,1527,1529,1531,1533,1535,1537,1540,1542,1545,1547,1550,1553,1555],{"class":692,"line":825},[690,1509,1188],{"class":700},[690,1511,1191],{"class":704},[690,1513,961],{"class":700},[690,1515,1197],{"class":1196},[690,1517,1137],{"class":757},[690,1519,721],{"class":700},[690,1521,1522],{"class":717},"order-metrics",[690,1524,721],{"class":700},[690,1526,1474],{"class":700},[690,1528,1455],{"class":704},[690,1530,961],{"class":700},[690,1532,945],{"class":704},[690,1534,1474],{"class":700},[690,1536,933],{"class":700},[690,1538,1539],{"class":717},"Recorded ",[690,1541,939],{"class":700},[690,1543,1544],{"class":704},"totalUnits",[690,1546,948],{"class":700},[690,1548,1549],{"class":717}," unit(s)",[690,1551,1552],{"class":700},"`",[690,1554,1152],{"class":757},[690,1556,724],{"class":700},[690,1558,1559],{"class":692,"line":845},[690,1560,904],{"class":700},[690,1562,1563],{"class":692,"line":861},[690,1564,787],{"class":700},[499,1566,1567,1568,1571],{},"The base you bind to ",[514,1569,1570],{},"is"," the policy, so there is no flag to get wrong.",[506,1573,1575],{"id":1574},"_3-publish-from-the-controller","3. Publish from the controller",[499,1577,1578,1579,1582,1583,1586,1587,1590,1591,1593,1594,1596],{},"The publisher injects ",[564,1580,1581],{},"EventBus"," (provided for you, by class identity) and emits, so the shop has no idea\nwho's listening. Use ",[564,1584,1585],{},"emitAsync"," to ",[558,1588,1589],{},"await"," the fan-out, so a serial handler's throw surfaces here on the\nrequest's success path. Because ",[564,1592,1123],{}," extends ",[564,1595,651],{},", the live instance emits with no\ncast — the base carries the dispatch stamp at the type level.",[680,1598,1601],{"className":682,"code":1599,"filename":1600,"language":685,"meta":686,"style":686},"import { EventBus } from \"@heximon\u002Fevents\";\nimport type { Controller, Post } from \"@heximon\u002Fhttp\";\nimport { type OrderLineItem, OrderPlacedEvent } from \".\u002Forder-events\";\n\ninterface PlaceOrderBody {\n  readonly customerEmail: string;\n  readonly lineItems: readonly OrderLineItem[];\n}\n\nexport class ShopController implements Controller\u003C\"\u002Fshop\"> {\n  public constructor(private readonly eventBus: EventBus) {}\n\n  public async placeOrder(action: Post\u003C\"\u002Forders\">): Promise\u003C{ orderId: string; status: \"placed\" }> {\n    const body = (await action.request.json()) as PlaceOrderBody;\n    const orderId = `order-${Date.now().toString(36)}`;\n\n    \u002F\u002F Fan out to every handler bound to OrderPlacedEvent; emitAsync awaits them all.\n    await this.eventBus.emitAsync(new OrderPlacedEvent(orderId, body.customerEmail, body.lineItems));\n\n    return { orderId, status: \"placed\" };\n  }\n}\n","src\u002Fshop\u002Fshop.controller.ts",[564,1602,1603,1624,1653,1680,1684,1694,1706,1722,1726,1730,1756,1779,1783,1843,1879,1920,1924,1929,1976,1980,2003,2008],{"__ignoreMap":686},[690,1604,1605,1607,1609,1612,1614,1616,1618,1620,1622],{"class":692,"line":693},[690,1606,697],{"class":696},[690,1608,701],{"class":700},[690,1610,1611],{"class":704}," EventBus",[690,1613,708],{"class":700},[690,1615,711],{"class":696},[690,1617,714],{"class":700},[690,1619,718],{"class":717},[690,1621,721],{"class":700},[690,1623,724],{"class":700},[690,1625,1626,1628,1630,1632,1635,1637,1640,1642,1644,1646,1649,1651],{"class":692,"line":727},[690,1627,697],{"class":696},[690,1629,1034],{"class":696},[690,1631,701],{"class":700},[690,1633,1634],{"class":704}," Controller",[690,1636,1474],{"class":700},[690,1638,1639],{"class":704}," Post",[690,1641,708],{"class":700},[690,1643,711],{"class":696},[690,1645,714],{"class":700},[690,1647,1648],{"class":717},"@heximon\u002Fhttp",[690,1650,721],{"class":700},[690,1652,724],{"class":700},[690,1654,1655,1657,1659,1661,1663,1665,1667,1669,1671,1673,1676,1678],{"class":692,"line":734},[690,1656,697],{"class":696},[690,1658,701],{"class":700},[690,1660,1034],{"class":696},[690,1662,745],{"class":704},[690,1664,1474],{"class":700},[690,1666,803],{"class":704},[690,1668,708],{"class":700},[690,1670,711],{"class":696},[690,1672,714],{"class":700},[690,1674,1675],{"class":717},".\u002Forder-events",[690,1677,721],{"class":700},[690,1679,724],{"class":700},[690,1681,1682],{"class":692,"line":751},[690,1683,731],{"emptyLinePlaceholder":730},[690,1685,1686,1689,1692],{"class":692,"line":769},[690,1687,1688],{"class":740},"interface",[690,1690,1691],{"class":744}," PlaceOrderBody",[690,1693,748],{"class":700},[690,1695,1696,1698,1700,1702,1704],{"class":692,"line":784},[690,1697,754],{"class":740},[690,1699,852],{"class":757},[690,1701,761],{"class":700},[690,1703,764],{"class":744},[690,1705,724],{"class":700},[690,1707,1708,1710,1712,1714,1716,1718,1720],{"class":692,"line":790},[690,1709,754],{"class":740},[690,1711,868],{"class":757},[690,1713,761],{"class":700},[690,1715,831],{"class":740},[690,1717,745],{"class":744},[690,1719,877],{"class":704},[690,1721,724],{"class":700},[690,1723,1724],{"class":692,"line":795},[690,1725,787],{"class":700},[690,1727,1728],{"class":692,"line":813},[690,1729,731],{"emptyLinePlaceholder":730},[690,1731,1732,1734,1736,1739,1741,1743,1745,1747,1750,1752,1754],{"class":692,"line":825},[690,1733,737],{"class":696},[690,1735,800],{"class":740},[690,1737,1738],{"class":744}," ShopController",[690,1740,1115],{"class":740},[690,1742,1634],{"class":744},[690,1744,1120],{"class":700},[690,1746,721],{"class":700},[690,1748,1749],{"class":717},"\u002Fshop",[690,1751,721],{"class":700},[690,1753,1126],{"class":700},[690,1755,748],{"class":700},[690,1757,1758,1760,1762,1764,1766,1768,1771,1773,1775,1777],{"class":692,"line":845},[690,1759,816],{"class":740},[690,1761,819],{"class":740},[690,1763,1137],{"class":700},[690,1765,1140],{"class":740},[690,1767,831],{"class":740},[690,1769,1770],{"class":834}," eventBus",[690,1772,761],{"class":700},[690,1774,1611],{"class":744},[690,1776,1152],{"class":700},[690,1778,1155],{"class":700},[690,1780,1781],{"class":692,"line":861},[690,1782,731],{"emptyLinePlaceholder":730},[690,1784,1785,1787,1789,1792,1794,1797,1799,1801,1803,1805,1808,1810,1813,1815,1818,1820,1822,1824,1826,1829,1831,1833,1836,1838,1841],{"class":692,"line":882},[690,1786,816],{"class":740},[690,1788,1417],{"class":740},[690,1790,1791],{"class":757}," placeOrder",[690,1793,1137],{"class":700},[690,1795,1796],{"class":834},"action",[690,1798,761],{"class":700},[690,1800,1639],{"class":744},[690,1802,1120],{"class":700},[690,1804,721],{"class":700},[690,1806,1807],{"class":717},"\u002Forders",[690,1809,721],{"class":700},[690,1811,1812],{"class":700},">):",[690,1814,1432],{"class":744},[690,1816,1817],{"class":700},"\u003C{",[690,1819,835],{"class":757},[690,1821,761],{"class":700},[690,1823,764],{"class":744},[690,1825,1073],{"class":700},[690,1827,1828],{"class":757}," status",[690,1830,761],{"class":700},[690,1832,714],{"class":700},[690,1834,1835],{"class":717},"placed",[690,1837,721],{"class":700},[690,1839,1840],{"class":700}," }>",[690,1842,748],{"class":700},[690,1844,1845,1847,1850,1852,1854,1856,1859,1861,1864,1866,1869,1872,1875,1877],{"class":692,"line":890},[690,1846,1446],{"class":740},[690,1848,1849],{"class":704}," body",[690,1851,1452],{"class":700},[690,1853,951],{"class":757},[690,1855,1589],{"class":696},[690,1857,1858],{"class":704}," action",[690,1860,961],{"class":700},[690,1862,1863],{"class":704},"request",[690,1865,961],{"class":700},[690,1867,1868],{"class":1196},"json",[690,1870,1871],{"class":757},"()) ",[690,1873,1874],{"class":696},"as",[690,1876,1691],{"class":744},[690,1878,724],{"class":700},[690,1880,1881,1883,1885,1887,1889,1892,1894,1897,1899,1902,1904,1906,1909,1911,1914,1916,1918],{"class":692,"line":901},[690,1882,1446],{"class":740},[690,1884,835],{"class":704},[690,1886,1452],{"class":700},[690,1888,933],{"class":700},[690,1890,1891],{"class":717},"order-",[690,1893,939],{"class":700},[690,1895,1896],{"class":704},"Date",[690,1898,961],{"class":700},[690,1900,1901],{"class":1196},"now",[690,1903,896],{"class":704},[690,1905,961],{"class":700},[690,1907,1908],{"class":1196},"toString",[690,1910,1137],{"class":704},[690,1912,1913],{"class":1500},"36",[690,1915,1152],{"class":704},[690,1917,979],{"class":700},[690,1919,724],{"class":700},[690,1921,1922],{"class":692,"line":907},[690,1923,731],{"emptyLinePlaceholder":730},[690,1925,1926],{"class":692,"line":912},[690,1927,1928],{"class":1076},"    \u002F\u002F Fan out to every handler bound to OrderPlacedEvent; emitAsync awaits them all.\n",[690,1930,1931,1934,1937,1940,1942,1944,1946,1949,1951,1953,1955,1957,1959,1961,1963,1965,1967,1969,1971,1974],{"class":692,"line":927},[690,1932,1933],{"class":696},"    await",[690,1935,1936],{"class":700}," this.",[690,1938,1939],{"class":704},"eventBus",[690,1941,961],{"class":700},[690,1943,1585],{"class":1196},[690,1945,1137],{"class":757},[690,1947,1948],{"class":700},"new",[690,1950,803],{"class":1196},[690,1952,1137],{"class":757},[690,1954,945],{"class":704},[690,1956,1474],{"class":700},[690,1958,1849],{"class":704},[690,1960,961],{"class":700},[690,1962,976],{"class":704},[690,1964,1474],{"class":700},[690,1966,1849],{"class":704},[690,1968,961],{"class":700},[690,1970,958],{"class":704},[690,1972,1973],{"class":757},"))",[690,1975,724],{"class":700},[690,1977,1978],{"class":692,"line":984},[690,1979,731],{"emptyLinePlaceholder":730},[690,1981,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000],{"class":692,"line":989},[690,1983,930],{"class":696},[690,1985,701],{"class":700},[690,1987,835],{"class":704},[690,1989,1474],{"class":700},[690,1991,1828],{"class":757},[690,1993,761],{"class":700},[690,1995,714],{"class":700},[690,1997,1835],{"class":717},[690,1999,721],{"class":700},[690,2001,2002],{"class":700}," };\n",[690,2004,2006],{"class":692,"line":2005},21,[690,2007,904],{"class":700},[690,2009,2011],{"class":692,"line":2010},22,[690,2012,787],{"class":700},[499,2014,2015,2016,2019,2020,2023],{},"Want the response to return ",[514,2017,2018],{},"before"," the handlers finish? ",[564,2021,2022],{},"eventBus.emit(...)"," is the fire-and-forget\nvariant — the handlers run to completion, but the reply doesn't block on them.",[506,2025,2027],{"id":2026},"_4-wire-the-two-modules","4. Wire the two modules",[499,2029,2030,2031,2034,2035,2038,2039,2042,2043,2046,2047,2050,2051,961],{},"Register handlers under the ",[564,2032,2033],{},"events"," namespace — serial ones under ",[564,2036,2037],{},"handlers",", parallel-absorb ones under\n",[564,2040,2041],{},"notificationHandlers",", never in ",[564,2044,2045],{},"providers",". That placement is what marks them as subscribers rather than\nplain injectables; their dependencies (the shared ",[564,2048,2049],{},"EventLog",") do go in ",[564,2052,2045],{},[680,2054,2057],{"className":682,"code":2055,"filename":2056,"language":685,"meta":686,"style":686},"import { Module } from \"@heximon\u002Fruntime\";\nimport { ConfirmationEmailHandler } from \".\u002Fconfirmation-email.handler\";\nimport { EventLog } from \".\u002Fevent-log\";\nimport { OrderMetricsHandler } from \".\u002Forder-metrics.handler\";\n\nexport class NotificationsModule extends Module({\n  providers: [EventLog],\n  events: {\n    handlers: [ConfirmationEmailHandler], \u002F\u002F serial — a throw rolls back the publisher\n    notificationHandlers: [OrderMetricsHandler], \u002F\u002F parallel-absorb — failures are swallowed\n  },\n  exports: [EventLog],\n}) {}\n","src\u002Fnotifications\u002Fnotifications.module.ts",[564,2058,2059,2081,2102,2122,2143,2147,2165,2177,2186,2201,2216,2221,2232],{"__ignoreMap":686},[690,2060,2061,2063,2065,2068,2070,2072,2074,2077,2079],{"class":692,"line":693},[690,2062,697],{"class":696},[690,2064,701],{"class":700},[690,2066,2067],{"class":704}," Module",[690,2069,708],{"class":700},[690,2071,711],{"class":696},[690,2073,714],{"class":700},[690,2075,2076],{"class":717},"@heximon\u002Fruntime",[690,2078,721],{"class":700},[690,2080,724],{"class":700},[690,2082,2083,2085,2087,2089,2091,2093,2095,2098,2100],{"class":692,"line":727},[690,2084,697],{"class":696},[690,2086,701],{"class":700},[690,2088,1112],{"class":704},[690,2090,708],{"class":700},[690,2092,711],{"class":696},[690,2094,714],{"class":700},[690,2096,2097],{"class":717},".\u002Fconfirmation-email.handler",[690,2099,721],{"class":700},[690,2101,724],{"class":700},[690,2103,2104,2106,2108,2110,2112,2114,2116,2118,2120],{"class":692,"line":734},[690,2105,697],{"class":696},[690,2107,701],{"class":700},[690,2109,1086],{"class":704},[690,2111,708],{"class":700},[690,2113,711],{"class":696},[690,2115,714],{"class":700},[690,2117,1095],{"class":717},[690,2119,721],{"class":700},[690,2121,724],{"class":700},[690,2123,2124,2126,2128,2130,2132,2134,2136,2139,2141],{"class":692,"line":751},[690,2125,697],{"class":696},[690,2127,701],{"class":700},[690,2129,1372],{"class":704},[690,2131,708],{"class":700},[690,2133,711],{"class":696},[690,2135,714],{"class":700},[690,2137,2138],{"class":717},".\u002Forder-metrics.handler",[690,2140,721],{"class":700},[690,2142,724],{"class":700},[690,2144,2145],{"class":692,"line":769},[690,2146,731],{"emptyLinePlaceholder":730},[690,2148,2149,2151,2153,2156,2158,2160,2162],{"class":692,"line":784},[690,2150,737],{"class":696},[690,2152,800],{"class":740},[690,2154,2155],{"class":744}," NotificationsModule",[690,2157,806],{"class":740},[690,2159,2067],{"class":1196},[690,2161,1137],{"class":704},[690,2163,2164],{"class":700},"{\n",[690,2166,2167,2170,2172,2175],{"class":692,"line":790},[690,2168,2169],{"class":757},"  providers",[690,2171,761],{"class":700},[690,2173,2174],{"class":704}," [EventLog]",[690,2176,842],{"class":700},[690,2178,2179,2182,2184],{"class":692,"line":795},[690,2180,2181],{"class":757},"  events",[690,2183,761],{"class":700},[690,2185,748],{"class":700},[690,2187,2188,2191,2193,2196,2198],{"class":692,"line":813},[690,2189,2190],{"class":757},"    handlers",[690,2192,761],{"class":700},[690,2194,2195],{"class":704}," [ConfirmationEmailHandler]",[690,2197,1474],{"class":700},[690,2199,2200],{"class":1076}," \u002F\u002F serial — a throw rolls back the publisher\n",[690,2202,2203,2206,2208,2211,2213],{"class":692,"line":825},[690,2204,2205],{"class":757},"    notificationHandlers",[690,2207,761],{"class":700},[690,2209,2210],{"class":704}," [OrderMetricsHandler]",[690,2212,1474],{"class":700},[690,2214,2215],{"class":1076}," \u002F\u002F parallel-absorb — failures are swallowed\n",[690,2217,2218],{"class":692,"line":845},[690,2219,2220],{"class":700},"  },\n",[690,2222,2223,2226,2228,2230],{"class":692,"line":861},[690,2224,2225],{"class":757},"  exports",[690,2227,761],{"class":700},[690,2229,2174],{"class":704},[690,2231,842],{"class":700},[690,2233,2234,2236,2239],{"class":692,"line":882},[690,2235,948],{"class":700},[690,2237,2238],{"class":704},") ",[690,2240,2241],{"class":700},"{}\n",[499,2243,2244],{},"The shop module owns only the publisher and never imports the notifications module:",[680,2246,2249],{"className":682,"code":2247,"filename":2248,"language":685,"meta":686,"style":686},"import { Module } from \"@heximon\u002Fruntime\";\nimport { ShopController } from \".\u002Fshop.controller\";\n\nexport class ShopModule extends Module({\n  http: { controllers: [ShopController] },\n}) {}\n","src\u002Fshop\u002Fshop.module.ts",[564,2250,2251,2271,2292,2296,2313,2333],{"__ignoreMap":686},[690,2252,2253,2255,2257,2259,2261,2263,2265,2267,2269],{"class":692,"line":693},[690,2254,697],{"class":696},[690,2256,701],{"class":700},[690,2258,2067],{"class":704},[690,2260,708],{"class":700},[690,2262,711],{"class":696},[690,2264,714],{"class":700},[690,2266,2076],{"class":717},[690,2268,721],{"class":700},[690,2270,724],{"class":700},[690,2272,2273,2275,2277,2279,2281,2283,2285,2288,2290],{"class":692,"line":727},[690,2274,697],{"class":696},[690,2276,701],{"class":700},[690,2278,1738],{"class":704},[690,2280,708],{"class":700},[690,2282,711],{"class":696},[690,2284,714],{"class":700},[690,2286,2287],{"class":717},".\u002Fshop.controller",[690,2289,721],{"class":700},[690,2291,724],{"class":700},[690,2293,2294],{"class":692,"line":734},[690,2295,731],{"emptyLinePlaceholder":730},[690,2297,2298,2300,2302,2305,2307,2309,2311],{"class":692,"line":751},[690,2299,737],{"class":696},[690,2301,800],{"class":740},[690,2303,2304],{"class":744}," ShopModule",[690,2306,806],{"class":740},[690,2308,2067],{"class":1196},[690,2310,1137],{"class":704},[690,2312,2164],{"class":700},[690,2314,2315,2318,2320,2322,2325,2327,2330],{"class":692,"line":769},[690,2316,2317],{"class":757},"  http",[690,2319,761],{"class":700},[690,2321,701],{"class":700},[690,2323,2324],{"class":757}," controllers",[690,2326,761],{"class":700},[690,2328,2329],{"class":704}," [ShopController] ",[690,2331,2332],{"class":700},"},\n",[690,2334,2335,2337,2339],{"class":692,"line":784},[690,2336,948],{"class":700},[690,2338,2238],{"class":704},[690,2340,2241],{"class":700},[499,2342,2343,2344,2347],{},"The root module composes both, and the bus is the only seam between them. Drop ",[564,2345,2346],{},"NotificationsModule"," and\nthe shop happily emits into the void — no compile error, because nothing imports across the boundary:",[680,2349,2352],{"className":682,"code":2350,"filename":2351,"language":685,"meta":686,"style":686},"import { Module } from \"@heximon\u002Fruntime\";\nimport { NotificationsModule } from \".\u002Fnotifications\u002Fnotifications.module\";\nimport { ShopModule } from \".\u002Fshop\u002Fshop.module\";\n\nexport class AppModule extends Module({\n  imports: [ShopModule, NotificationsModule],\n}) {}\n","src\u002Fapp.module.ts",[564,2353,2354,2374,2395,2416,2420,2437,2454],{"__ignoreMap":686},[690,2355,2356,2358,2360,2362,2364,2366,2368,2370,2372],{"class":692,"line":693},[690,2357,697],{"class":696},[690,2359,701],{"class":700},[690,2361,2067],{"class":704},[690,2363,708],{"class":700},[690,2365,711],{"class":696},[690,2367,714],{"class":700},[690,2369,2076],{"class":717},[690,2371,721],{"class":700},[690,2373,724],{"class":700},[690,2375,2376,2378,2380,2382,2384,2386,2388,2391,2393],{"class":692,"line":727},[690,2377,697],{"class":696},[690,2379,701],{"class":700},[690,2381,2155],{"class":704},[690,2383,708],{"class":700},[690,2385,711],{"class":696},[690,2387,714],{"class":700},[690,2389,2390],{"class":717},".\u002Fnotifications\u002Fnotifications.module",[690,2392,721],{"class":700},[690,2394,724],{"class":700},[690,2396,2397,2399,2401,2403,2405,2407,2409,2412,2414],{"class":692,"line":734},[690,2398,697],{"class":696},[690,2400,701],{"class":700},[690,2402,2304],{"class":704},[690,2404,708],{"class":700},[690,2406,711],{"class":696},[690,2408,714],{"class":700},[690,2410,2411],{"class":717},".\u002Fshop\u002Fshop.module",[690,2413,721],{"class":700},[690,2415,724],{"class":700},[690,2417,2418],{"class":692,"line":751},[690,2419,731],{"emptyLinePlaceholder":730},[690,2421,2422,2424,2426,2429,2431,2433,2435],{"class":692,"line":769},[690,2423,737],{"class":696},[690,2425,800],{"class":740},[690,2427,2428],{"class":744}," AppModule",[690,2430,806],{"class":740},[690,2432,2067],{"class":1196},[690,2434,1137],{"class":704},[690,2436,2164],{"class":700},[690,2438,2439,2442,2444,2447,2449,2452],{"class":692,"line":784},[690,2440,2441],{"class":757},"  imports",[690,2443,761],{"class":700},[690,2445,2446],{"class":704}," [ShopModule",[690,2448,1474],{"class":700},[690,2450,2451],{"class":704}," NotificationsModule]",[690,2453,842],{"class":700},[690,2455,2456,2458,2460],{"class":692,"line":790},[690,2457,948],{"class":700},[690,2459,2238],{"class":704},[690,2461,2241],{"class":700},[506,2463,2465],{"id":2464},"_5-register-the-plugin-and-run-it","5. Register the plugin and run it",[499,2467,2468,2469,761],{},"Add the events compiler plugin next to the HTTP plugin in ",[564,2470,2471],{},"heximon.config.ts",[680,2473,2475],{"className":682,"code":2474,"filename":2471,"language":685,"meta":686,"style":686},"import { defineHeximonConfig } from \"@heximon\u002Fbuild\";\nimport { EventEmitterPlugin } from \"@heximon\u002Fevents\u002Fcompiler\";\nimport { HttpPlugin } from \"@heximon\u002Fhttp\u002Fcompiler\";\n\nexport default defineHeximonConfig({\n  plugins: [new HttpPlugin(), new EventEmitterPlugin()],\n});\n",[564,2476,2477,2499,2521,2543,2547,2560,2588],{"__ignoreMap":686},[690,2478,2479,2481,2483,2486,2488,2490,2492,2495,2497],{"class":692,"line":693},[690,2480,697],{"class":696},[690,2482,701],{"class":700},[690,2484,2485],{"class":704}," defineHeximonConfig",[690,2487,708],{"class":700},[690,2489,711],{"class":696},[690,2491,714],{"class":700},[690,2493,2494],{"class":717},"@heximon\u002Fbuild",[690,2496,721],{"class":700},[690,2498,724],{"class":700},[690,2500,2501,2503,2505,2508,2510,2512,2514,2517,2519],{"class":692,"line":727},[690,2502,697],{"class":696},[690,2504,701],{"class":700},[690,2506,2507],{"class":704}," EventEmitterPlugin",[690,2509,708],{"class":700},[690,2511,711],{"class":696},[690,2513,714],{"class":700},[690,2515,2516],{"class":717},"@heximon\u002Fevents\u002Fcompiler",[690,2518,721],{"class":700},[690,2520,724],{"class":700},[690,2522,2523,2525,2527,2530,2532,2534,2536,2539,2541],{"class":692,"line":734},[690,2524,697],{"class":696},[690,2526,701],{"class":700},[690,2528,2529],{"class":704}," HttpPlugin",[690,2531,708],{"class":700},[690,2533,711],{"class":696},[690,2535,714],{"class":700},[690,2537,2538],{"class":717},"@heximon\u002Fhttp\u002Fcompiler",[690,2540,721],{"class":700},[690,2542,724],{"class":700},[690,2544,2545],{"class":692,"line":751},[690,2546,731],{"emptyLinePlaceholder":730},[690,2548,2549,2551,2554,2556,2558],{"class":692,"line":769},[690,2550,737],{"class":696},[690,2552,2553],{"class":696}," default",[690,2555,2485],{"class":1196},[690,2557,1137],{"class":704},[690,2559,2164],{"class":700},[690,2561,2562,2565,2567,2570,2572,2574,2576,2578,2581,2583,2586],{"class":692,"line":784},[690,2563,2564],{"class":757},"  plugins",[690,2566,761],{"class":700},[690,2568,2569],{"class":704}," [",[690,2571,1948],{"class":700},[690,2573,2529],{"class":1196},[690,2575,896],{"class":704},[690,2577,1474],{"class":700},[690,2579,2580],{"class":700}," new",[690,2582,2507],{"class":1196},[690,2584,2585],{"class":704},"()]",[690,2587,842],{"class":700},[690,2589,2590,2592,2594],{"class":692,"line":790},[690,2591,948],{"class":700},[690,2593,1152],{"class":704},[690,2595,724],{"class":700},[499,2597,2598,2601],{},[564,2599,2600],{},"vite.config.ts"," itself stays unchanged — the plugin takes no arguments:",[680,2603,2605],{"className":682,"code":2604,"filename":2600,"language":685,"meta":686,"style":686},"import heximon from \"@heximon\u002Fbuild\u002Fvite\";\nimport { defineConfig } from \"vite-plus\";\n\nexport default defineConfig({ plugins: [heximon()] });\n",[564,2606,2607,2626,2648,2652],{"__ignoreMap":686},[690,2608,2609,2611,2614,2617,2619,2622,2624],{"class":692,"line":693},[690,2610,697],{"class":696},[690,2612,2613],{"class":704}," heximon ",[690,2615,2616],{"class":696},"from",[690,2618,714],{"class":700},[690,2620,2621],{"class":717},"@heximon\u002Fbuild\u002Fvite",[690,2623,721],{"class":700},[690,2625,724],{"class":700},[690,2627,2628,2630,2632,2635,2637,2639,2641,2644,2646],{"class":692,"line":727},[690,2629,697],{"class":696},[690,2631,701],{"class":700},[690,2633,2634],{"class":704}," defineConfig",[690,2636,708],{"class":700},[690,2638,711],{"class":696},[690,2640,714],{"class":700},[690,2642,2643],{"class":717},"vite-plus",[690,2645,721],{"class":700},[690,2647,724],{"class":700},[690,2649,2650],{"class":692,"line":734},[690,2651,731],{"emptyLinePlaceholder":730},[690,2653,2654,2656,2658,2660,2662,2665,2668,2670,2672,2675,2678,2680,2682],{"class":692,"line":751},[690,2655,737],{"class":696},[690,2657,2553],{"class":696},[690,2659,2634],{"class":1196},[690,2661,1137],{"class":704},[690,2663,2664],{"class":700},"{",[690,2666,2667],{"class":757}," plugins",[690,2669,761],{"class":700},[690,2671,2569],{"class":704},[690,2673,2674],{"class":1196},"heximon",[690,2676,2677],{"class":704},"()] ",[690,2679,948],{"class":700},[690,2681,1152],{"class":704},[690,2683,724],{"class":700},[499,2685,2686],{},"Then place an order and watch the fan-out:",[680,2688,2693],{"className":2689,"code":2690,"filename":2691,"language":2692,"meta":686,"style":686},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -s -X POST localhost:3000\u002Fshop\u002Forders \\\n  -H 'content-type: application\u002Fjson' \\\n  -d '{\"customerEmail\":\"alice@example.com\",\"lineItems\":[{\"productId\":\"p1\",\"quantity\":2}]}'\n# → { \"orderId\": \"...\", \"status\": \"placed\" }\n# the confirmation email (serial) ran before the response; the metrics handler fanned out too\n","terminal","bash",[564,2694,2695,2715,2731,2744,2749],{"__ignoreMap":686},[690,2696,2697,2700,2703,2706,2709,2712],{"class":692,"line":693},[690,2698,2699],{"class":744},"curl",[690,2701,2702],{"class":717}," -s",[690,2704,2705],{"class":717}," -X",[690,2707,2708],{"class":717}," POST",[690,2710,2711],{"class":717}," localhost:3000\u002Fshop\u002Forders",[690,2713,2714],{"class":704}," \\\n",[690,2716,2717,2720,2723,2726,2729],{"class":692,"line":727},[690,2718,2719],{"class":717},"  -H",[690,2721,2722],{"class":700}," '",[690,2724,2725],{"class":717},"content-type: application\u002Fjson",[690,2727,2728],{"class":700},"'",[690,2730,2714],{"class":704},[690,2732,2733,2736,2738,2741],{"class":692,"line":734},[690,2734,2735],{"class":717},"  -d",[690,2737,2722],{"class":700},[690,2739,2740],{"class":717},"{\"customerEmail\":\"alice@example.com\",\"lineItems\":[{\"productId\":\"p1\",\"quantity\":2}]}",[690,2742,2743],{"class":700},"'\n",[690,2745,2746],{"class":692,"line":751},[690,2747,2748],{"class":1076},"# → { \"orderId\": \"...\", \"status\": \"placed\" }\n",[690,2750,2751],{"class":692,"line":769},[690,2752,2753],{"class":1076},"# the confirmation email (serial) ran before the response; the metrics handler fanned out too\n",[506,2755,2757],{"id":2756},"cross-a-boundary-with-the-integration-tier","Cross a boundary with the integration tier",[499,2759,2760,2761,2764,2765,2768,2769,961],{},"When a reaction must outlive the request — a welcome email after sign-up, a downstream service that\nconsumes your events — move it to the ",[558,2762,2763],{},"integration tier",". You declare a typed ",[564,2766,2767],{},"IntegrationEvent"," producer\nclass, inject it by class identity, and call ",[564,2770,2771],{},"publish(data)",[499,2773,2774,2775,2778,2779,2782],{},"The compiler wires the publisher over the bound ",[564,2776,2777],{},"IntegrationEventTransport"," (the in-process\n",[564,2780,2781],{},"MemoryIntegrationEventTransport"," default, assembled by the host at boot — zero boilerplate). A handler\nclass per consumer drains the channel.",[499,2784,2785],{},"The producer class carries the event's name and payload as type arguments, so a wrong name or payload shape\nis a compile error — there is no separate shared map or string key to keep in sync:",[680,2787,2790],{"className":682,"code":2788,"filename":2789,"language":685,"meta":686,"style":686},"import { IntegrationEvent } from \"@heximon\u002Fintegration\";\n\nexport interface UserSignedUpPayload {\n  readonly userId: string;\n  readonly name: string;\n  readonly email: string;\n}\n\nexport class UserSignedUp extends IntegrationEvent\u003C\"user.signed-up\", UserSignedUpPayload> {}\n","src\u002Fusers\u002Fevents.ts",[564,2791,2792,2814,2818,2829,2842,2855,2868,2872,2876],{"__ignoreMap":686},[690,2793,2794,2796,2798,2801,2803,2805,2807,2810,2812],{"class":692,"line":693},[690,2795,697],{"class":696},[690,2797,701],{"class":700},[690,2799,2800],{"class":704}," IntegrationEvent",[690,2802,708],{"class":700},[690,2804,711],{"class":696},[690,2806,714],{"class":700},[690,2808,2809],{"class":717},"@heximon\u002Fintegration",[690,2811,721],{"class":700},[690,2813,724],{"class":700},[690,2815,2816],{"class":692,"line":727},[690,2817,731],{"emptyLinePlaceholder":730},[690,2819,2820,2822,2824,2827],{"class":692,"line":734},[690,2821,737],{"class":696},[690,2823,741],{"class":740},[690,2825,2826],{"class":744}," UserSignedUpPayload",[690,2828,748],{"class":700},[690,2830,2831,2833,2836,2838,2840],{"class":692,"line":751},[690,2832,754],{"class":740},[690,2834,2835],{"class":757}," userId",[690,2837,761],{"class":700},[690,2839,764],{"class":744},[690,2841,724],{"class":700},[690,2843,2844,2846,2849,2851,2853],{"class":692,"line":769},[690,2845,754],{"class":740},[690,2847,2848],{"class":757}," name",[690,2850,761],{"class":700},[690,2852,764],{"class":744},[690,2854,724],{"class":700},[690,2856,2857,2859,2862,2864,2866],{"class":692,"line":784},[690,2858,754],{"class":740},[690,2860,2861],{"class":757}," email",[690,2863,761],{"class":700},[690,2865,764],{"class":744},[690,2867,724],{"class":700},[690,2869,2870],{"class":692,"line":790},[690,2871,787],{"class":700},[690,2873,2874],{"class":692,"line":795},[690,2875,731],{"emptyLinePlaceholder":730},[690,2877,2878,2880,2882,2885,2887,2889,2891,2893,2896,2898,2900,2902,2904],{"class":692,"line":813},[690,2879,737],{"class":696},[690,2881,800],{"class":740},[690,2883,2884],{"class":744}," UserSignedUp",[690,2886,806],{"class":740},[690,2888,2800],{"class":744},[690,2890,1120],{"class":700},[690,2892,721],{"class":700},[690,2894,2895],{"class":717},"user.signed-up",[690,2897,721],{"class":700},[690,2899,1474],{"class":700},[690,2901,2826],{"class":744},[690,2903,1126],{"class":700},[690,2905,1155],{"class":700},[499,2907,2908,2909,2912,2913,2916],{},"The controller injects the typed event by class identity and publishes — ",[564,2910,2911],{},"await publish(data)"," hands the\nwork off to the transport and resolves, so the sign-up responds ",[564,2914,2915],{},"201"," without waiting on the email:",[680,2918,2921],{"className":682,"code":2919,"filename":2920,"language":685,"meta":686,"style":686},"import { uuid } from \"@heximon\u002Fruntime\";\nimport type { Controller, Post } from \"@heximon\u002Fhttp\";\nimport { UserSignedUp } from \".\u002Fevents\";\n\nexport class SignupController implements Controller\u003C\"\u002Fusers\"> {\n  private readonly userSignedUp: UserSignedUp;\n\n  public constructor(userSignedUp: UserSignedUp) {\n    this.userSignedUp = userSignedUp;\n  }\n\n  public async signUp(action: Post\u003C\"\u002F\">): Promise\u003C{ id: string; email: string }> {\n    const body = (await action.request.json()) as { name: string; email: string };\n    const userId = uuid.v7();\n\n    await this.userSignedUp.publish({ userId, name: body.name, email: body.email });\n\n    action.response.status = 201;\n    return { id: userId, email: body.email };\n  }\n}\n","src\u002Fusers\u002Fsignup.controller.ts",[564,2922,2923,2944,2970,2991,2995,3021,3037,3041,3060,3072,3076,3080,3131,3175,3194,3198,3249,3253,3275,3301,3305],{"__ignoreMap":686},[690,2924,2925,2927,2929,2932,2934,2936,2938,2940,2942],{"class":692,"line":693},[690,2926,697],{"class":696},[690,2928,701],{"class":700},[690,2930,2931],{"class":704}," uuid",[690,2933,708],{"class":700},[690,2935,711],{"class":696},[690,2937,714],{"class":700},[690,2939,2076],{"class":717},[690,2941,721],{"class":700},[690,2943,724],{"class":700},[690,2945,2946,2948,2950,2952,2954,2956,2958,2960,2962,2964,2966,2968],{"class":692,"line":727},[690,2947,697],{"class":696},[690,2949,1034],{"class":696},[690,2951,701],{"class":700},[690,2953,1634],{"class":704},[690,2955,1474],{"class":700},[690,2957,1639],{"class":704},[690,2959,708],{"class":700},[690,2961,711],{"class":696},[690,2963,714],{"class":700},[690,2965,1648],{"class":717},[690,2967,721],{"class":700},[690,2969,724],{"class":700},[690,2971,2972,2974,2976,2978,2980,2982,2984,2987,2989],{"class":692,"line":734},[690,2973,697],{"class":696},[690,2975,701],{"class":700},[690,2977,2884],{"class":704},[690,2979,708],{"class":700},[690,2981,711],{"class":696},[690,2983,714],{"class":700},[690,2985,2986],{"class":717},".\u002Fevents",[690,2988,721],{"class":700},[690,2990,724],{"class":700},[690,2992,2993],{"class":692,"line":751},[690,2994,731],{"emptyLinePlaceholder":730},[690,2996,2997,2999,3001,3004,3006,3008,3010,3012,3015,3017,3019],{"class":692,"line":769},[690,2998,737],{"class":696},[690,3000,800],{"class":740},[690,3002,3003],{"class":744}," SignupController",[690,3005,1115],{"class":740},[690,3007,1634],{"class":744},[690,3009,1120],{"class":700},[690,3011,721],{"class":700},[690,3013,3014],{"class":717},"\u002Fusers",[690,3016,721],{"class":700},[690,3018,1126],{"class":700},[690,3020,748],{"class":700},[690,3022,3023,3026,3028,3031,3033,3035],{"class":692,"line":784},[690,3024,3025],{"class":740},"  private",[690,3027,831],{"class":740},[690,3029,3030],{"class":757}," userSignedUp",[690,3032,761],{"class":700},[690,3034,2884],{"class":744},[690,3036,724],{"class":700},[690,3038,3039],{"class":692,"line":790},[690,3040,731],{"emptyLinePlaceholder":730},[690,3042,3043,3045,3047,3049,3052,3054,3056,3058],{"class":692,"line":795},[690,3044,816],{"class":740},[690,3046,819],{"class":740},[690,3048,1137],{"class":700},[690,3050,3051],{"class":834},"userSignedUp",[690,3053,761],{"class":700},[690,3055,2884],{"class":744},[690,3057,1152],{"class":700},[690,3059,748],{"class":700},[690,3061,3062,3064,3066,3068,3070],{"class":692,"line":813},[690,3063,1188],{"class":700},[690,3065,3051],{"class":704},[690,3067,1452],{"class":700},[690,3069,3030],{"class":704},[690,3071,724],{"class":700},[690,3073,3074],{"class":692,"line":825},[690,3075,904],{"class":700},[690,3077,3078],{"class":692,"line":845},[690,3079,731],{"emptyLinePlaceholder":730},[690,3081,3082,3084,3086,3089,3091,3093,3095,3097,3099,3101,3104,3106,3108,3110,3112,3115,3117,3119,3121,3123,3125,3127,3129],{"class":692,"line":861},[690,3083,816],{"class":740},[690,3085,1417],{"class":740},[690,3087,3088],{"class":757}," signUp",[690,3090,1137],{"class":700},[690,3092,1796],{"class":834},[690,3094,761],{"class":700},[690,3096,1639],{"class":744},[690,3098,1120],{"class":700},[690,3100,721],{"class":700},[690,3102,3103],{"class":717},"\u002F",[690,3105,721],{"class":700},[690,3107,1812],{"class":700},[690,3109,1432],{"class":744},[690,3111,1817],{"class":700},[690,3113,3114],{"class":757}," id",[690,3116,761],{"class":700},[690,3118,764],{"class":744},[690,3120,1073],{"class":700},[690,3122,2861],{"class":757},[690,3124,761],{"class":700},[690,3126,764],{"class":744},[690,3128,1840],{"class":700},[690,3130,748],{"class":700},[690,3132,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3155,3157,3159,3161,3163,3165,3167,3169,3171,3173],{"class":692,"line":882},[690,3134,1446],{"class":740},[690,3136,1849],{"class":704},[690,3138,1452],{"class":700},[690,3140,951],{"class":757},[690,3142,1589],{"class":696},[690,3144,1858],{"class":704},[690,3146,961],{"class":700},[690,3148,1863],{"class":704},[690,3150,961],{"class":700},[690,3152,1868],{"class":1196},[690,3154,1871],{"class":757},[690,3156,1874],{"class":696},[690,3158,701],{"class":700},[690,3160,2848],{"class":757},[690,3162,761],{"class":700},[690,3164,764],{"class":744},[690,3166,1073],{"class":700},[690,3168,2861],{"class":757},[690,3170,761],{"class":700},[690,3172,764],{"class":744},[690,3174,2002],{"class":700},[690,3176,3177,3179,3181,3183,3185,3187,3190,3192],{"class":692,"line":890},[690,3178,1446],{"class":740},[690,3180,2835],{"class":704},[690,3182,1452],{"class":700},[690,3184,2931],{"class":704},[690,3186,961],{"class":700},[690,3188,3189],{"class":1196},"v7",[690,3191,896],{"class":757},[690,3193,724],{"class":700},[690,3195,3196],{"class":692,"line":901},[690,3197,731],{"emptyLinePlaceholder":730},[690,3199,3200,3202,3204,3206,3208,3211,3213,3215,3217,3219,3221,3223,3225,3227,3230,3232,3234,3236,3238,3240,3243,3245,3247],{"class":692,"line":907},[690,3201,1933],{"class":696},[690,3203,1936],{"class":700},[690,3205,3051],{"class":704},[690,3207,961],{"class":700},[690,3209,3210],{"class":1196},"publish",[690,3212,1137],{"class":757},[690,3214,2664],{"class":700},[690,3216,2835],{"class":704},[690,3218,1474],{"class":700},[690,3220,2848],{"class":757},[690,3222,761],{"class":700},[690,3224,1849],{"class":704},[690,3226,961],{"class":700},[690,3228,3229],{"class":704},"name",[690,3231,1474],{"class":700},[690,3233,2861],{"class":757},[690,3235,761],{"class":700},[690,3237,1849],{"class":704},[690,3239,961],{"class":700},[690,3241,3242],{"class":704},"email",[690,3244,708],{"class":700},[690,3246,1152],{"class":757},[690,3248,724],{"class":700},[690,3250,3251],{"class":692,"line":912},[690,3252,731],{"emptyLinePlaceholder":730},[690,3254,3255,3258,3260,3263,3265,3268,3270,3273],{"class":692,"line":927},[690,3256,3257],{"class":704},"    action",[690,3259,961],{"class":700},[690,3261,3262],{"class":704},"response",[690,3264,961],{"class":700},[690,3266,3267],{"class":704},"status",[690,3269,1452],{"class":700},[690,3271,3272],{"class":1500}," 201",[690,3274,724],{"class":700},[690,3276,3277,3279,3281,3283,3285,3287,3289,3291,3293,3295,3297,3299],{"class":692,"line":984},[690,3278,930],{"class":696},[690,3280,701],{"class":700},[690,3282,3114],{"class":757},[690,3284,761],{"class":700},[690,3286,2835],{"class":704},[690,3288,1474],{"class":700},[690,3290,2861],{"class":757},[690,3292,761],{"class":700},[690,3294,1849],{"class":704},[690,3296,961],{"class":700},[690,3298,3242],{"class":704},[690,3300,2002],{"class":700},[690,3302,3303],{"class":692,"line":989},[690,3304,904],{"class":700},[690,3306,3307],{"class":692,"line":2005},[690,3308,787],{"class":700},[499,3310,3311,3312,3315],{},"List the producer under ",[564,3313,3314],{},"integration: { events }"," in its owning module — the compiler wires its\npublisher and makes it injectable. No transport boilerplate is needed in the simple case:",[680,3317,3320],{"className":682,"code":3318,"filename":3319,"language":685,"meta":686,"style":686},"import { Module } from \"@heximon\u002Fruntime\";\nimport { UserSignedUp } from \".\u002Fevents\";\nimport { SignupController } from \".\u002Fsignup.controller\";\n\nexport class UsersModule extends Module({\n  http: { controllers: [SignupController] },\n  integration: { events: [UserSignedUp] },\n}) {}\n","src\u002Fusers\u002Fusers.module.ts",[564,3321,3322,3342,3362,3383,3387,3404,3421,3440],{"__ignoreMap":686},[690,3323,3324,3326,3328,3330,3332,3334,3336,3338,3340],{"class":692,"line":693},[690,3325,697],{"class":696},[690,3327,701],{"class":700},[690,3329,2067],{"class":704},[690,3331,708],{"class":700},[690,3333,711],{"class":696},[690,3335,714],{"class":700},[690,3337,2076],{"class":717},[690,3339,721],{"class":700},[690,3341,724],{"class":700},[690,3343,3344,3346,3348,3350,3352,3354,3356,3358,3360],{"class":692,"line":727},[690,3345,697],{"class":696},[690,3347,701],{"class":700},[690,3349,2884],{"class":704},[690,3351,708],{"class":700},[690,3353,711],{"class":696},[690,3355,714],{"class":700},[690,3357,2986],{"class":717},[690,3359,721],{"class":700},[690,3361,724],{"class":700},[690,3363,3364,3366,3368,3370,3372,3374,3376,3379,3381],{"class":692,"line":734},[690,3365,697],{"class":696},[690,3367,701],{"class":700},[690,3369,3003],{"class":704},[690,3371,708],{"class":700},[690,3373,711],{"class":696},[690,3375,714],{"class":700},[690,3377,3378],{"class":717},".\u002Fsignup.controller",[690,3380,721],{"class":700},[690,3382,724],{"class":700},[690,3384,3385],{"class":692,"line":751},[690,3386,731],{"emptyLinePlaceholder":730},[690,3388,3389,3391,3393,3396,3398,3400,3402],{"class":692,"line":769},[690,3390,737],{"class":696},[690,3392,800],{"class":740},[690,3394,3395],{"class":744}," UsersModule",[690,3397,806],{"class":740},[690,3399,2067],{"class":1196},[690,3401,1137],{"class":704},[690,3403,2164],{"class":700},[690,3405,3406,3408,3410,3412,3414,3416,3419],{"class":692,"line":784},[690,3407,2317],{"class":757},[690,3409,761],{"class":700},[690,3411,701],{"class":700},[690,3413,2324],{"class":757},[690,3415,761],{"class":700},[690,3417,3418],{"class":704}," [SignupController] ",[690,3420,2332],{"class":700},[690,3422,3423,3426,3428,3430,3433,3435,3438],{"class":692,"line":790},[690,3424,3425],{"class":757},"  integration",[690,3427,761],{"class":700},[690,3429,701],{"class":700},[690,3431,3432],{"class":757}," events",[690,3434,761],{"class":700},[690,3436,3437],{"class":704}," [UserSignedUp] ",[690,3439,2332],{"class":700},[690,3441,3442,3444,3446],{"class":692,"line":795},[690,3443,948],{"class":700},[690,3445,2238],{"class":704},[690,3447,2241],{"class":700},[499,3449,3450,3451,3454,3455,3458],{},"The consumer ",[564,3452,3453],{},"implements IntegrationEventHandler\u003C\"user.signed-up\", Payload>","; its ",[564,3456,3457],{},"handle"," runs later, off the\nrequest path, when the transport delivers the event on its channel:",[680,3460,3463],{"className":682,"code":3461,"filename":3462,"language":685,"meta":686,"style":686},"import type { IntegrationEventHandler } from \"@heximon\u002Fintegration\";\nimport type { UserSignedUpPayload } from \"..\u002Fusers\u002Fevents\";\nimport { CaptureEmailTransport } from \".\u002Fcapture-email-transport\";\n\ntype SignedUpPayload = UserSignedUpPayload;\n\nexport class WelcomeEmailHandler implements IntegrationEventHandler\u003C\"user.signed-up\", SignedUpPayload> {\n  private readonly transport: CaptureEmailTransport;\n\n  public constructor(transport: CaptureEmailTransport) {\n    this.transport = transport;\n  }\n\n  public async handle(payload: SignedUpPayload): Promise\u003Cvoid> {\n    \u002F\u002F ...compose and send the welcome email through the injected transport\n  }\n}\n","src\u002Fmail\u002Fwelcome-email.handler.ts",[564,3464,3465,3488,3511,3533,3537,3551,3555,3584,3599,3603,3622,3634,3638,3642,3671,3676,3680],{"__ignoreMap":686},[690,3466,3467,3469,3471,3473,3476,3478,3480,3482,3484,3486],{"class":692,"line":693},[690,3468,697],{"class":696},[690,3470,1034],{"class":696},[690,3472,701],{"class":700},[690,3474,3475],{"class":704}," IntegrationEventHandler",[690,3477,708],{"class":700},[690,3479,711],{"class":696},[690,3481,714],{"class":700},[690,3483,2809],{"class":717},[690,3485,721],{"class":700},[690,3487,724],{"class":700},[690,3489,3490,3492,3494,3496,3498,3500,3502,3504,3507,3509],{"class":692,"line":727},[690,3491,697],{"class":696},[690,3493,1034],{"class":696},[690,3495,701],{"class":700},[690,3497,2826],{"class":704},[690,3499,708],{"class":700},[690,3501,711],{"class":696},[690,3503,714],{"class":700},[690,3505,3506],{"class":717},"..\u002Fusers\u002Fevents",[690,3508,721],{"class":700},[690,3510,724],{"class":700},[690,3512,3513,3515,3517,3520,3522,3524,3526,3529,3531],{"class":692,"line":734},[690,3514,697],{"class":696},[690,3516,701],{"class":700},[690,3518,3519],{"class":704}," CaptureEmailTransport",[690,3521,708],{"class":700},[690,3523,711],{"class":696},[690,3525,714],{"class":700},[690,3527,3528],{"class":717},".\u002Fcapture-email-transport",[690,3530,721],{"class":700},[690,3532,724],{"class":700},[690,3534,3535],{"class":692,"line":751},[690,3536,731],{"emptyLinePlaceholder":730},[690,3538,3539,3542,3545,3547,3549],{"class":692,"line":769},[690,3540,3541],{"class":740},"type",[690,3543,3544],{"class":744}," SignedUpPayload",[690,3546,1452],{"class":700},[690,3548,2826],{"class":744},[690,3550,724],{"class":700},[690,3552,3553],{"class":692,"line":784},[690,3554,731],{"emptyLinePlaceholder":730},[690,3556,3557,3559,3561,3564,3566,3568,3570,3572,3574,3576,3578,3580,3582],{"class":692,"line":790},[690,3558,737],{"class":696},[690,3560,800],{"class":740},[690,3562,3563],{"class":744}," WelcomeEmailHandler",[690,3565,1115],{"class":740},[690,3567,3475],{"class":744},[690,3569,1120],{"class":700},[690,3571,721],{"class":700},[690,3573,2895],{"class":717},[690,3575,721],{"class":700},[690,3577,1474],{"class":700},[690,3579,3544],{"class":744},[690,3581,1126],{"class":700},[690,3583,748],{"class":700},[690,3585,3586,3588,3590,3593,3595,3597],{"class":692,"line":795},[690,3587,3025],{"class":740},[690,3589,831],{"class":740},[690,3591,3592],{"class":757}," transport",[690,3594,761],{"class":700},[690,3596,3519],{"class":744},[690,3598,724],{"class":700},[690,3600,3601],{"class":692,"line":813},[690,3602,731],{"emptyLinePlaceholder":730},[690,3604,3605,3607,3609,3611,3614,3616,3618,3620],{"class":692,"line":825},[690,3606,816],{"class":740},[690,3608,819],{"class":740},[690,3610,1137],{"class":700},[690,3612,3613],{"class":834},"transport",[690,3615,761],{"class":700},[690,3617,3519],{"class":744},[690,3619,1152],{"class":700},[690,3621,748],{"class":700},[690,3623,3624,3626,3628,3630,3632],{"class":692,"line":845},[690,3625,1188],{"class":700},[690,3627,3613],{"class":704},[690,3629,1452],{"class":700},[690,3631,3592],{"class":704},[690,3633,724],{"class":700},[690,3635,3636],{"class":692,"line":861},[690,3637,904],{"class":700},[690,3639,3640],{"class":692,"line":882},[690,3641,731],{"emptyLinePlaceholder":730},[690,3643,3644,3646,3648,3650,3652,3655,3657,3659,3661,3663,3665,3667,3669],{"class":692,"line":890},[690,3645,816],{"class":740},[690,3647,1417],{"class":740},[690,3649,1166],{"class":757},[690,3651,1137],{"class":700},[690,3653,3654],{"class":834},"payload",[690,3656,761],{"class":700},[690,3658,3544],{"class":744},[690,3660,1178],{"class":700},[690,3662,1432],{"class":744},[690,3664,1120],{"class":700},[690,3666,1437],{"class":744},[690,3668,1126],{"class":700},[690,3670,748],{"class":700},[690,3672,3673],{"class":692,"line":901},[690,3674,3675],{"class":1076},"    \u002F\u002F ...compose and send the welcome email through the injected transport\n",[690,3677,3678],{"class":692,"line":907},[690,3679,904],{"class":700},[690,3681,3682],{"class":692,"line":912},[690,3683,787],{"class":700},[499,3685,3686,3687,3690,3691,3694,3695,3698,3699,3701,3702,3704],{},"Integration handlers are declared under ",[564,3688,3689],{},"integration: { eventHandlers }",". For ",[558,3692,3693],{},"transactional delivery","\n— where the integration event must commit atomically with the aggregate write and survive crashes —\nswap in ",[564,3696,3697],{},"OutboxIntegrationEventTransport"," (the ",[564,3700,2809],{}," transactional-outbox transport\nsatisfier) by providing it as the ",[564,3703,2777],{}," binding in your module.",[499,3706,3707,3708,961],{},"The in-process memory default applies when no transport is explicitly bound. Wiring that end to end is\nits own recipe: ",[675,3709,3710],{"href":462},"Background jobs",[506,3712,3714],{"id":3713},"see-also","See also",[3716,3717,3718,3724,3733,3743,3754],"ul",{},[3719,3720,3721,3723],"li",{},[675,3722,3710],{"href":462}," — the integration tier and a typed queue, drained in memory\nfor dev and durable in production.",[3719,3725,3726,3728,3729,3732],{},[675,3727,677],{"href":471}," — record domain events on an aggregate and flush them\nwith ",[564,3730,3731],{},"DomainEvents"," after the write commits.",[3719,3734,3735,3738,3739,3742],{},[675,3736,3737],{"href":147},"Event-driven architecture"," — the full bus contract: the tiers, delivery policies,\nand the string-keyed ",[564,3740,3741],{},"EventMap"," for dynamically-named events.",[3719,3744,3745,3753],{},[675,3746,3750],{"href":3747,"rel":3748},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fladder\u002FL03-events",[3749],"nofollow",[558,3751,3752],{},"the ladder's L03 — Events","\n— a publisher with no handler imports, serial and parallel-absorb subscribers, and a test asserting\nexactly which handlers ran, in which order.",[3719,3755,3756,3763],{},[675,3757,3760],{"href":3758,"rel":3759},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fladder\u002FL08-queue",[3749],[558,3761,3762],{},"the ladder's L08 — Queue","\n— the integration tier end to end: sign-up emits, the queue drains, and a welcome email is sent off the\nrequest path.",[3765,3766,3767],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":686,"searchDepth":727,"depth":727,"links":3769},[3770,3771,3772,3773,3774,3775,3776,3777],{"id":508,"depth":727,"text":509},{"id":644,"depth":727,"text":645},{"id":994,"depth":727,"text":995},{"id":1574,"depth":727,"text":1575},{"id":2026,"depth":727,"text":2027},{"id":2464,"depth":727,"text":2465},{"id":2756,"depth":727,"text":2757},{"id":3713,"depth":727,"text":3714},"Wire an event-driven module with EventBus, EventHandler and NotificationHandler, the events namespace, DomainEvents, and IntegrationEventHandler across the in-process, domain, and integration tiers.","md",null,{},{"icon":149},{"title":466,"description":3778},"u_EyfR-tA8-_rjkqhWbAmdSzOd-QUHrNr0jfi_uKIU4",[3786,3788],{"title":461,"path":462,"stem":463,"description":3787,"icon":464,"children":-1},"Move work off the request path with a typed IntegrationEvent producer, an IntegrationEventHandler consumer, an in-process MemoryIntegrationEventTransport default, and an optional transactional-outbox tier for at-least-once delivery.",{"title":470,"path":471,"stem":472,"description":3789,"icon":473,"children":-1},"Model an Order aggregate root with branded ids, value objects, invariants, DomainEvent recording, DomainEvents.emitFrom flushing, and a cross-context DomainEventHandler.",1783300761903]