Event-driven architecture

On this page 7

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.

ComponentRoleTypical implementations
Event producerDetects a state change and emits an eventApplication services, devices, database change capture
Event brokerRoutes, buffers and delivers eventsMessage brokers, streaming platforms, cloud event buses
Event consumerSubscribes and performs a reactionMicroservices, functions, analytics jobs
Event schemaDefines the structure of event dataJSON Schema, Avro, Protocol Buffers, CloudEvents
Core building blocks

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 typeContentWhen it fits
Event notificationMinimal data, often only an identifierConsumers fetch details only when needed
Event-carried state transferFull relevant data in the eventConsumers keep local copies and avoid callbacks
Domain eventA business fact in domain languageIntegration between bounded business areas
Change data capture eventRow-level change from a database logIntegrating legacy systems without code changes
Common kinds of events

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 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.

ConceptMain ideaRelation to event-driven architecture
Request-responseCaller waits for a direct answerSynchronous alternative with tighter coupling
Event sourcingEvents are the system of record for stateOptional persistence pattern built on events
CQRSSeparate models for writes and readsRead models are often updated from events
Service-oriented architectureShared services, often through an enterprise busEarlier style that also used messaging
Publish-subscribeMessages delivered to all subscribersThe core messaging pattern behind most event flows
Related concepts compared

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.