event-driven-architecture · git:20260720.67d6135 · 2026-07-20 · sha256 1f873bf54cbbebc6
event-driven-architecture git:20260720.67d6135A
Immutable. This exact content is served forever at /api/v1/blob/1f873bf54cbbebc6.
--- name: event-driven-architecture description: Design event-driven systems with well-shaped events, schema contracts, and honest handling of choreography's costs. Use when decoupling services through events or debugging an event system nobody can reason about. --- # Event-driven architecture Events invert control: producers announce what happened and forget; consumers react on their own schedule. This buys decoupling and scalability at the price of a system whose behavior lives in no single place, so the design discipline is making that distributed behavior traceable and its contracts stable. ## Method 1. **Model events as immutable business facts.** Past tense, named for what happened (`OrderPlaced`, not `OrderService.update`), carrying the identifiers and the facts consumers need, with a version and timestamp (see event-sourcing's event design; the same rule whether or not you store the log). Events are commands' opposite: they announce, they do not instruct, and they cannot be rejected. 2. **Choose choreography and orchestration deliberately.** Choreography (services react to events independently) scales and decouples but scatters the workflow: no one place answers "where is order 123" (see saga-pattern's same tradeoff). Orchestration (a coordinator drives the steps) keeps flows readable at the cost of a central component. Use choreography for genuinely independent reactions, orchestration for multi-step business processes: mixing them by accident is how event systems become unknowable. 3. **Treat event schemas as contracts.** Producers and consumers are coupled through the event shape; evolve it additively (new optional fields), version breaking changes, and enforce compatibility mechanically (schema registry: see schema-evolution, api-change-management). An event's schema is a public API with many invisible consumers. 4. **Deliver at-least-once, consume idempotently.** The transport redelivers; consumers dedupe or design naturally idempotent handlers (see idempotent-consumers, delivery-guarantees), and never assume ordering beyond per-key (see message-queues, clock-skew). Producers emit events reliably via the outbox pattern so state change and event never diverge (see transactional-outbox). 5. **Make the invisible traceable.** Correlation IDs threaded through every event and handler (see distributed-tracing), a catalog of events with their producers and consumers (see data-lineage's instinct for events), and observability that reconstructs a flow across services (see observability). Without these, an event system is a debugging nightmare where cause and effect live in different repos. 6. **Provide a reconciliation path.** Consumers miss events (downtime, bugs); a replayable log (see message-queues' retention) or a query API to re-fetch current state (see rest-endpoint-design) lets a recovered consumer catch up. Event-driven without a catch-up story loses data silently on every consumer outage. ## Boundaries - Events decouple, but eventual consistency is the price: the UI and users see intermediate states (see cqrs's read lag), and workflows are not atomic (see saga-pattern); design the UX for in-flight and the data for reconcile. - Not everything is an event: request/response is simpler and correct for synchronous needs (a user awaiting a result); reaching for events to decouple two services that must agree synchronously adds latency and complexity for nothing. - The broker becomes critical infrastructure with its own scaling, retention, and failure modes (see message-queues, capacity-planning); event-driven is a distributed-systems commitment, not a library import.