How it works
Start with a business event, not just a background task. An event such as OrderPlaced states a fact; a command such as SendReceipt asks a particular component to act. An event typically includes an event ID, entity ID, event type, schema version, and the data consumers need.
A producer commits a state change and reliably publishes the corresponding event. Writing the database and publishing to a broker separately creates a failure window. A transactional outbox closes that window by saving the business change and an outbox record in one database transaction, then having a relay publish the record.
A broker routes or retains events for consumers. With publish-subscribe, billing, notifications, and analytics can each receive OrderPlaced through separate subscriptions. Within one subscription or consumer group, multiple workers can share the processing load rather than each processing every event.
Consumers update their own state or trigger more work, then acknowledge successful processing. A crash after a side effect but before acknowledgment can cause redelivery, so handlers need idempotent effects. For database updates, recording the event ID and applying the update in the same transaction is one common approach.
Define ordering only where the business needs it. For example, route an order's events through the same ordered partition, and preserve that order during processing. Include entity versions when consumers must reject stale updates; broker ordering alone does not protect against concurrent processing or reordered retries.
Operate the pipeline as a system with backlog and partial failure. Monitor consumer lag, event age, failures, and end-to-end completion time. Use bounded retries, dead-letter handling, compatible schema changes, and controlled replay; broker retention determines how far consumers can recover.
A worked example
Keeping order notifications off the checkout path
Suppose checkout accepts 200 orders per second. Each transaction saves an order and an OrderPlaced outbox record. A relay publishes those events to a durable broker, with separate consumer groups for email and analytics. Checkout returns after the database commit, not after email delivery. If the email provider is unavailable for 10 minutes, about 120,000 email events accumulate while analytics can continue. After recovery, email workers process 500 events per second; with 200 new events per second arriving, the backlog drains in roughly 400 seconds. Workers deduplicate by event ID, use provider idempotency when available, and reconcile uncertain send outcomes rather than assuming retries cannot send twice.
Trade-offs to say out loud
- Independent scaling and failure isolation
- Consumers can scale and recover separately, but a broker outage or shared resource bottleneck can still affect multiple services.
- Shorter request paths, delayed completion
- The API can return after durable acceptance, but downstream results may appear later and need explicit pending states or freshness targets.
- Loose runtime coupling, lasting data contracts
- Producers need not call every consumer, but event schemas and business meanings become contracts that must evolve compatibly.
- Reliable delivery requires duplicate-safe effects
- At-least-once delivery supports recovery, but consumers must handle duplicates and uncertain external outcomes without blindly repeating side effects.
- Ordering limits parallelism
- Serializing work for one entity simplifies correctness, but a hot entity can become a bottleneck even when other partitions have spare capacity.
- More operational complexity
- Queues buffer bursts rather than create capacity, so teams must manage backlog, poison events, replay safety, and tracing across asynchronous boundaries.
When to reach for it
- One committed change must trigger several independent reactions, such as feed updates, notifications, and analytics.
- Slow or unreliable work should not block an interactive request, such as transcoding media or calling an email provider.
- Traffic arrives in bursts, and downstream workers need durable buffering and independently adjustable processing capacity.
- The design needs continuously updated derived views, search indexes, aggregates, or recommendations with an explicit staleness budget.
- A business process spans services and can represent intermediate states instead of requiring one immediate cross-service transaction.
How to bring it up in the interview
- Identify the synchronous boundary first: explain what must be durable before the API returns and which effects can complete later. Interviewers will probe whether “accepted” is being confused with “finished.”
- Walk through one event end to end, naming the producer, publication mechanism, topic or queue, consumer groups, state changes, and acknowledgment point.
- Defend the failure behavior with a crash example: ask what happens after the database commit, after publication, and after a consumer's side effect but before acknowledgment.
- Specify delivery and ordering scopes rather than promising “exactly once” or “ordered events.” Expect follow-ups about duplicate payments, stale updates, hot partitions, and replay.
- Give an event-age target and a recovery plan. Explain how lag is detected, how throughput catches up, and what happens when a consumer cannot recover before retention expires.
Common mistakes
- Adding a broker to every interaction, including operations that need an immediate result and are simpler as synchronous calls.
- Publishing an event separately from the database commit without explaining how missing or premature events are prevented.
- Assuming all subscribers receive a copy when they actually share one work queue or consumer group.
- Treating broker delivery guarantees as guarantees that an external side effect, such as a charge or email, happens exactly once.
- Confusing event-driven architecture with event sourcing, or assuming asynchronous events enforce cross-service business invariants automatically.
Where it comes up
Interview questions where event-driven architecture is part of a strong answer, and why.
- Design a notification systemBusiness events feed independent delivery pipelines that need durable processing, retries, and duplicate-safe sends.
- Design a news feed like FacebookPost events drive asynchronous fan-out into followers' feeds, making publication reliability and delayed visibility central concerns.
- Design a video streaming service like YouTubeUpload and processing-completion events coordinate transcoding stages and determine when a video can be published safely.
- Design a food delivery app like DoorDashOrder, restaurant, payment, and courier events advance a distributed lifecycle that must tolerate retries and partial failure.
- Design an ad click aggregatorClick events drive continuously updated aggregates whose correctness depends on duplicates, late arrivals, and replay.
- Design Google AnalyticsCollected events flow through independent processing stages for sessionization, aggregation, and analytical storage.
- Design a price alert systemPrice events trigger stateful alert evaluation followed by reliable notification delivery across an asynchronous boundary.
Questions people ask
What is the difference between event-driven architecture and a message queue?
Event-driven architecture is a way to organize how components react to changes. A message queue is infrastructure that can support it, but queues also carry commands and background jobs. Publish-subscribe topics and retained event logs are other common building blocks.
Is event-driven architecture the same as event sourcing?
No. Event-driven architecture describes communication and reactions between components; a service can still store its current state in ordinary database tables. Event sourcing stores an entity's authoritative history as events and derives its state from that history.
When should you avoid event-driven architecture?
Avoid introducing asynchronous boundaries when the caller needs an immediate answer or a local transaction already solves the problem cleanly. Strict invariants, such as preventing oversold inventory, still need an authoritative concurrency-control mechanism. Events can distribute the resulting changes, but they do not replace that mechanism.