Event-driven architecture is useful when a durable fact should trigger independent work without keeping the originating request open. It is not a synonym for “put a queue between services.” The design begins with business ownership, durability, delivery, ordering, and recovery decisions.
Quick answer
Use an event when a producer can publish a durable fact and consumers can react under an explicit delay, duplicate, ordering, and compatibility contract. Keep a synchronous request when the caller needs an immediate authoritative decision. A broker can retain and redeliver records, but it cannot make an external payment, email, or database update exactly once by itself.
Prerequisites
Read Message Queues Explained for broker fundamentals and Database Transactions Explained for local commit boundaries. This lesson assumes you can distinguish a local database transaction from a remote network call.
Start from the business decision
A command asks an owner to attempt work: ReserveInventory. An event states that something durable happened: InventoryReserved. Naming a wish as a completed fact lets downstream consumers act on state that never committed. Naming every internal change as a public event exposes implementation detail and makes evolution expensive.
For each message, record the producer, authoritative state, acceptance point, consumers, maximum useful delay, ordering key, retention need, sensitive fields, and recovery owner. If the team cannot name the durable fact or the operator responsible for stuck work, asynchronous delivery is hiding uncertainty rather than managing it.
Choose sync and async boundaries deliberately
The order API may synchronously validate identity and reserve the local order number because the caller needs a definite response. It can then publish OrderAccepted for inventory, analytics, and notification work. The response proves only the local outcome. It does not prove that every consumer completed.
Asynchrony helps isolate latency and temporary outages, but it introduces queue age, duplicate delivery, replay, schema compatibility, and partial progress. A request that simply waits for a reply message still has a synchronous dependency with more moving parts. Use that pattern only when broker routing or durable buffering is a real requirement.
Design the acceptance point
The producer must not report durable publication merely because an in-memory callback ran. Common safe boundaries include a broker acknowledgment with documented durability or a local transaction that commits business state and an outbox record. The Transactional Outbox Pattern closes the database-versus-publication gap but normally permits duplicate publication after a relay crash.
The consumer must decide when acknowledgment becomes safe. ACK before processing can lose work after a crash. ACK after a durable side effect allows redelivery when the process crashes between the effect and ACK. That is why Message Delivery Semantics and an Idempotent Consumer belong in the architecture, not in a late operational checklist.
Model ordering and convergence
Global ordering is rarely available at useful scale. Define the smallest entity that needs order, such as orderId, and route related changes through the same ordering domain when the broker supports it. Consumers should still reject stale versions or converge through an explicit merge rule because retries, replays, producer races, and migrations can expose old state.
An event log is evidence, not automatically the current business projection. If a projection can be rebuilt, document the starting offset, schema versions, side effects that must be suppressed during replay, and the condition that makes the rebuilt view trustworthy.
Failure scenario
An order service commits an order, publishes OrderAccepted, and returns 202. Inventory succeeds, notification times out, and analytics is hours behind. The order is not globally “successful” or “failed.” Each consumer has its own durable state and recovery contract. The customer-facing order status must be derived from business-owned state, not from the hope that every subscriber finished.
Common mistakes
- Treating a broker acknowledgment as proof that a consumer’s database or external API changed.
- Publishing mutable database rows without a stable event identity or schema contract.
- Retrying permanent validation failures forever instead of using bounded recovery.
- Assuming timestamps create a trustworthy global order across clocks and partitions.
- Adding events where a direct in-process call would be clearer and safer.
Production validation
Test crash windows before publication, after broker acceptance, before consumer effects, after effects but before ACK, and during replay. Measure publish failures, consumer lag, oldest message age, retry counts, dead-letter arrivals, duplicate suppression, and end-to-end business completion. Prove a degraded consumer does not block unrelated consumers or overload their shared database during recovery.
Sources
- Apache Kafka, “Design,” accessed 2026-08-18: https://kafka.apache.org/41/design/design/.
- CloudEvents Specification repository, accessed 2026-08-18: https://github.com/cloudevents/spec.
- AsyncAPI documentation, accessed 2026-08-18: https://www.asyncapi.com/docs/concepts/asyncapi-document.
Related reading
Continue through the Event-Driven Systems Learning Path, then study Event Design with CloudEvents and AsyncAPI and Event Ordering, Deduplication, and State Convergence. The broader reliability context remains available in System Design and the Event-Driven Systems topic cluster.