Event-driven architecture (EDA) is a way to build systems where work flows through events instead of tight, synchronous request chains. When something meaningful happens—a payment clears, a file lands in storage, a user signs up—a small record (the event) is published. Other services react on their own schedule. That separation is why EDA shows up in payment platforms, inventory systems, analytics pipelines, and anything that must stay available when one component slows down.
If you are evaluating EDA for a greenfield service or trying to untangle a monolith, the first question is not “which message broker should we buy?” It is whether your domain has clear events, multiple consumers, and tolerance for eventual consistency. When those three line up, events usually reduce coupling and make scaling more predictable.
How event-driven architecture differs from request/response
In a classic REST or RPC flow, Service A calls Service B and waits. Service B calls Service C. Latency stacks, failure domains merge, and deploy windows get scary because everything must move together.
EDA flips part of that model:
- Producer records a fact (“OrderPlaced”) and publishes it.
- Broker or log stores and routes the event.
- Consumers subscribe and run side effects (email, fulfillment, fraud checks).
The producer does not need a live connection to every downstream system. Consumers can be added or removed without changing the producer’s code, as long as the event contract stays stable.
That does not mean EDA removes complexity—it moves complexity into schema design, delivery guarantees, and observability.
Core building blocks
Events, commands, and queries
Teams new to EDA often mix these up:
| Type | Meaning | Example |
|---|---|---|
| Event | Something already happened | InvoicePaid |
| Command | Request to do work | SendReceiptEmail |
| Query | Read current state | GetOrderStatus |
Events should be immutable facts named in past tense. Commands belong in APIs or task queues when you need a single handler to accept or reject work.
Event bus vs event log
An event bus (Amazon EventBridge, Google Pub/Sub with routing) is great for fan-out and integration between teams. An event log (Kafka, Kinesis) keeps an ordered history consumers can replay—useful for analytics, audit, and rebuilding projections.
Many production systems use both: a log for source of truth and a bus for operational reactions.
Consumers and idempotency
Network retries and at-least-once delivery mean the same event may arrive twice. Every consumer should be idempotent: processing the same order_id twice must not double-charge or double-ship.
Practical patterns:
- Store processed event IDs in a deduplication table.
- Use natural keys (
payment_id) with unique constraints in the database. - Design handlers so the second run is a no-op.
When EDA is a good fit
EDA shines when:
- Multiple subsystems need the same signal (billing, CRM, data warehouse).
- Peak load varies and you want to absorb spikes in a queue.
- Third-party integrations are flaky and should not block the user-facing path.
- Auditability matters—you want a trail of what happened and when.
EDA is a weaker default when:
- You need strong consistency across reads in one user action.
- The domain is tiny (one service, one database) and unlikely to grow.
- Team skill in distributed debugging is limited and you cannot invest in tooling yet.
Reference flow: e-commerce order
Imagine a checkout API that must feel fast:
- API validates cart, writes
ordersrow asPENDING, publishesOrderPlaced. - Inventory consumer reserves stock; publishes
InventoryReservedorInventoryFailed. - Payment consumer charges card on
InventoryReserved. - Shipping consumer creates a label on
PaymentCaptured. - Email consumer sends confirmation on
OrderConfirmed.
The HTTP response can return after step 1 or 2 if the product accepts “we received your order” while payment finishes asynchronously—provided the UX explains pending state honestly.
Design choices that prevent pain
Event contracts and versioning
Treat event payloads like public APIs. Use explicit schema versioning (event_version: 2), add fields instead of renaming, and run contract tests in CI. Consumers should ignore unknown fields.
Ordering
Global ordering is expensive. Prefer partition keys (per customer_id or order_id) so related events stay ordered while the system scales horizontally.
Dead-letter queues and replay
Assume some consumers will fail. Route poison messages to a DLQ, alert on depth, and build a replay tool that can re-drive events after fixes. Without replay, operations teams restart services and hope—which does not scale.
Observability
Log correlation IDs from the HTTP request into event metadata. Trace across publish and consume spans. Dashboards should show lag per consumer group, not just CPU per service.
AWS-oriented example (conceptual)
On AWS, a common pattern is API Gateway or ALB → Lambda or ECS for the write path → EventBridge for routing → Lambda, Step Functions, or SQS workers per domain. DynamoDB streams or RDS change data capture can turn database mutations into events when legacy code cannot publish explicitly yet.
SQS adds buffering when consumers are slower than producers. FIFO queues help when strict ordering per entity matters, at the cost of throughput limits you must model.
None of this requires every service on day one. Many teams strangle a monolith by publishing events at repository boundaries first, then extracting consumers.
Common mistakes
Chatty events. Publishing FieldUpdated for every column creates noise. Batch or publish meaningful domain events.
Synchronous disguise. Calling a REST endpoint from inside an event handler for every step reintroduces coupling. Use events or workflows for multi-step processes.
Missing saga strategy. Distributed transactions are hard. Use sagas (choreography or orchestration) with compensating actions when a downstream step fails after earlier steps succeeded.
No ownership. If “the platform team” owns the bus but product teams own consumers without SLAs, incidents become political. Define on-call per consumer.
Choosing between choreography and orchestration
Choreography means services react to events without a central brain—simple at first, harder to visualize when flows span six teams. Orchestration (Step Functions, Temporal, Camunda) centralizes the workflow graph—easier to audit, but the orchestrator becomes a critical dependency.
Rule of thumb: start with choreography for two or three consumers; move to orchestration when compensations, timers, and human approvals appear in the same flow.
Security and compliance notes
Events often carry identifiers, email addresses, or payment metadata. Encrypt topics at rest where the broker supports it, restrict publish/subscribe IAM policies, and scrub payloads in logs. For regulated workloads, document data residency if events cross regions during fan-out.
Migration path from a monolith
A pragmatic sequence:
- Identify bounded contexts and natural nouns (Order, Subscription, Shipment).
- List facts each context already knows; start publishing those after DB commit.
- Build read models or sync jobs fed by events before splitting databases.
- Extract the highest-churn consumer first, not the most “pure” service on paper.
FAQ
Is event-driven the same as microservices?
No. You can run EDA inside a monolith (in-process event bus) or use microservices without events (sync HTTP everywhere). They are often combined but not equivalent.
Do I need Kafka?
Only if you need durable replay, high throughput, or stream processing. Many products start with a managed bus and SQS.
How do I test consumers?
Contract tests on schemas, in-memory brokers in integration tests, and fixture events checked into the repo. Replay production samples (redacted) in staging.
What about GDPR deletes?
Events may contain personal data. Plan retention on logs, encryption, and deletion workflows that propagate to derived stores—not just the primary database.
Event-driven architecture trades immediate simplicity for long-term flexibility. If your product’s growth path includes more integrations, more teams, and more unpredictable load, investing in clear events, idempotent consumers, and replayable infrastructure usually pays off faster than bolting another synchronous HTTP call onto an already fragile chain.
Further Reading
Discover more articles on similar topics across our network
Comments
Loading comments…