Event-driven architecture is a software architecture style in which components communicate by publishing events, records that something has happened, and other components react to those events asynchronously instead of being called directly. The producer of an event does not know which consumers exist or what they will do, which makes event-driven architecture the main pattern for loose coupling between services, applications and devices.
An event is a fact in the past tense, such as OrderPlaced, PaymentFailed or SensorThresholdExceeded. Systems built around such facts can add new behavior by attaching new consumers without changing the code that produces the events, which is the central practical benefit of the approach.
How events flow between producers and consumers
Every event-driven system has three roles: event producers that detect a change and emit an event, an event channel or broker that transports and often stores events, and event consumers that subscribe and react. The broker decouples the parties in time and space, because a consumer may be offline when an event is published and process it later.
Brokers come in two broad families. Message queues deliver each message to one consumer and remove it once acknowledged, which suits work distribution. Event logs or streams keep an ordered, retained sequence that many consumers read independently at their own position, which suits analytics, replay and integration.
| Component | Role | Typical implementations |
|---|---|---|
| Event producer | Detects a state change and emits an event | Application services, devices, database change capture |
| Event broker | Routes, buffers and delivers events | Message brokers, streaming platforms, cloud event buses |
| Event consumer | Subscribes and performs a reaction | Microservices, functions, analytics jobs |
| Event schema | Defines the structure of event data | JSON Schema, Avro, Protocol Buffers, CloudEvents |
Topologies and event types
Event-driven systems are organized in two main topologies. In choreography, each service listens for events and decides independently what to do, so the overall workflow emerges from many local reactions. In orchestration, a central coordinator listens to events and issues commands, which makes a multi-step process easier to trace at the cost of an additional component.
| Event type | Content | When it fits |
|---|---|---|
| Event notification | Minimal data, often only an identifier | Consumers fetch details only when needed |
| Event-carried state transfer | Full relevant data in the event | Consumers keep local copies and avoid callbacks |
| Domain event | A business fact in domain language | Integration between bounded business areas |
| Change data capture event | Row-level change from a database log | Integrating legacy systems without code changes |
The choice of event type has a direct effect on coupling and load. Thin notifications keep events small but cause consumers to call back the producer, which recreates synchronous dependency. Fat events remove callbacks but make the schema a public contract that must evolve carefully.
Event-driven architecture compared with related styles
Event-driven architecture is a communication style, so it combines with other architectural styles rather than replacing them. Microservices often use events to stay independent; a monolithic architecture can also publish internal events between modules. The comparison below clarifies the closest neighbors.
| Concept | Main idea | Relation to event-driven architecture |
|---|---|---|
| Request-response | Caller waits for a direct answer | Synchronous alternative with tighter coupling |
| Event sourcing | Events are the system of record for state | Optional persistence pattern built on events |
| CQRS | Separate models for writes and reads | Read models are often updated from events |
| Service-oriented architecture | Shared services, often through an enterprise bus | Earlier style that also used messaging |
| Publish-subscribe | Messages delivered to all subscribers | The core messaging pattern behind most event flows |
A frequent misunderstanding is to treat event-driven architecture and event sourcing as the same thing. A system can publish events for integration while storing its state in an ordinary relational database; event sourcing is a separate, stronger commitment that stores every change as an immutable event and rebuilds state by replaying them.
Delivery guarantees, ordering and consistency
The hardest engineering problems in event-driven systems concern delivery guarantees. Most brokers provide at-least-once delivery, so a consumer can receive the same event twice after a retry or failover. Consumers must therefore be idempotent, meaning that processing a duplicate leaves the result unchanged, typically by recording processed event identifiers.
Ordering is usually guaranteed only within a partition or key, for example all events for one customer, not across the whole system. Because each consumer updates its own data at its own pace, the system becomes eventually consistent: different services may briefly show different states, and user interfaces must be designed to tolerate that delay.
Publishing reliably is another trap. If a service writes to its database and then publishes an event, a crash between the two steps loses the event. The transactional outbox pattern solves this by writing the event into an outbox table in the same database transaction, with a separate relay publishing it afterwards.
When to use it and common mistakes
Event-driven architecture suits systems where many independent parties react to the same facts, where load arrives in bursts, or where integration must not block the main transaction. Typical cases are order processing, payment notifications, Internet of Things telemetry, audit trails and real-time analytics. It is a poor fit for simple workflows that need an immediate answer, such as validating a form field.
- Hidden workflows: pure choreography across many services makes it hard to answer what happens after an order is placed; document flows and use tracing.
- Uncontrolled schema changes: renaming or removing fields breaks consumers silently; use a schema registry and versioning.
- Missing dead-letter handling: events that repeatedly fail must be moved aside and monitored instead of blocking a queue.
- Commands disguised as events: an event named SendEmail couples producer and consumer; events should state facts, not instructions.
In everyday language
Event-driven architecture works like a notice board instead of a chain of phone calls. When something happens, one part of the system pins a short note on the board. For example, it might say that a customer just placed an order.
Every other part that cares about orders reads the note and does its own job. One prepares the package, another sends a receipt, another updates the stock count. Nobody has to wait for anyone else, and a new task can be added simply by letting one more reader watch the board.
Seen in the real world
An online retailer processing about 50,000 orders a day replaces a chain of direct calls between its checkout, warehouse, invoicing and email services. Previously, a slow invoicing service made customers wait at checkout, and an outage there blocked sales entirely. The team introduces an OrderPlaced event published to a streaming platform, using the transactional outbox pattern in the checkout database.
Warehouse, invoicing, email and a new fraud-scoring service each consume the event independently and handle duplicates by order identifier. Checkout now returns in a fraction of a second, an invoicing outage only delays invoices instead of stopping orders, and the fraud service was added without touching checkout code. On a holiday peak, events queue up briefly and are processed within minutes once consumers scale out.