Event-driven architecture (EDA) routes facts about state changes through events instead of making every system call every other system directly. Producers publish events. Consumers subscribe, process at their own pace, and own their local reaction.
EDA is useful when systems need to stay decoupled in time, ownership, or scale. It also introduces new design work around schemas, ordering, retries, and observability.
Core shape
flowchart LR OrderService["Order service"] -->|"OrderPlaced"| Broker["Message broker"] Broker --> Billing["Billing"] Broker --> Fulfillment["Fulfillment"] Broker --> Search["Search index"] Broker --> Analytics["Analytics"]
The producer does not know who consumes the event. That is the main source of flexibility, and also the reason event contracts need discipline.
Events, commands, and documents
Not every message is the same thing.
| Message type | Meaning | Example |
|---|---|---|
| Event | Something already happened | OrderPlaced |
| Command | Something should happen | CapturePayment |
| Document message | Current state for synchronization | OrderSnapshotUpdated |
Events are facts and should be named in the past tense. Commands have one logical owner. Mixing the two leads to unclear ownership.
Choreography and orchestration
In choreography, services react to events without a central workflow coordinator. This keeps coupling low, but the workflow can become hard to see.
In orchestration, a coordinator sends commands and records progress. This is easier to observe and change, but the coordinator becomes an important component. The saga pattern can use either style.
Design concerns
- Schema evolution. Events are long-lived contracts. Add fields safely and avoid changing meanings in place.
- Ordering. Keep ordering scoped to an aggregate or partition key. Global ordering is expensive.
- Delivery. Most systems use at-least-once delivery, so consumers need idempotency.
- Publication. Use the outbox pattern when database writes and event publication must not drift apart.
- Consumption. Use the inbox pattern when duplicate side effects would be expensive.
- Failure handling. Route permanent failures to a dead-letter queue with clear ownership.
When to use it
Use EDA when multiple capabilities need to react to a business fact, when producers should not wait for consumers, or when replay and auditability matter. It fits order flows, billing integration, search indexing, notifications, analytics, and cross-context synchronization.
Prefer direct request and response when the caller needs an immediate answer, the workflow is simple, or the team cannot yet operate asynchronous failure modes.
Trade-offs
EDA reduces runtime coupling but increases design and operational complexity. The happy path often looks cleaner than synchronous calls. The failure path is where the architecture is judged.
