Dictionary

Event-driven architecture

In event-driven architecture, services talk by emitting events like order.placed instead of calling each other directly, and anything interested reacts. You get loose coupling and independent scaling, at the price of eventual consistency and duplicate or out-of-order events that consumers have to handle.

What is event-driven architecture?

In event-driven architecture, services communicate by announcing that something happened instead of calling each other directly. A service emits an event, a small record of a fact such as an order being placed or an invoice being paid, and any other service that cares about that fact reacts to it. The service that emitted the event does not know or care who is listening.

Compare that with a direct call. In a request-response design, checkout calls the invoicing service, waits for a reply, then calls inventory, and so on, and every caller has to know the address of everything downstream. In an event-driven design, checkout publishes one order.placed event and moves on. Invoicing, inventory, and a Teams notifier each pick it up on their own.

An event is usually a compact record. In Apache Kafka, for example, an event has a key, a value, and a timestamp: a key of alice, a value recording that Alice paid 200 EUR to Bob, and the time it happened. The event states a fact in the past tense. It is not an instruction aimed at a named recipient, which is exactly what keeps producers and consumers independent.

Producers, brokers, and consumers

Three roles show up in almost every event-driven system.

A producer, also called a publisher, is the service that emits events. A webshop, an ERP, or a data pipeline can all be producers. Its only job is to state that something happened and hand the event off.

A broker or event router sits in the middle. It receives events and routes them to whoever should get them. Because the broker is the one thing producers and consumers both know about, it is where you filter, fan out, and audit traffic. Amazon EventBridge, Azure Event Grid, and an Apache Kafka cluster are all brokers of different shapes.

A consumer, or subscriber, reacts to events. One event can have many consumers: an order.placed event might trigger invoicing, decrement stock, and post a message to a channel, none of them aware of the others.

The reason for the middle layer is decoupling. You can add a fifth consumer, say a fraud check, without touching the producer or the other consumers.

Pub/sub versus point-to-point routing

Brokers route events in two broadly different ways, and the difference decides what your system can and cannot do.

In publish-subscribe, or pub/sub, the broker copies every event to every interested subscriber. Ten subscribers to order.placed means ten deliveries. This is how one customer-created event reaches billing, CRM, and analytics at once. Azure Event Grid and Amazon SNS work this way. In a plain pub/sub broker the event is not stored after delivery, so a subscriber that connects tomorrow never sees today's events.

In point-to-point routing, each message goes to exactly one consumer. A message queue holds the work, and a pool of competing consumers pulls from it so the load spreads across instances and each message is handled once. This fits work that must happen a single time, like charging a card, rather than a fan-out notification.

A third model sits close by and is worth separating out: the durable event log. Events are appended to an ordered, retained log, and consumers track their own read position rather than subscribing. Apache Kafka, Azure Event Hubs, and Microsoft Fabric's Eventstream work this way. Because the log keeps events, a consumer can join late and replay history from any point, which plain pub/sub cannot do. Kafka guarantees order only within a partition, not across a whole topic, a detail that starts to matter the moment you run a consumer as several instances.

Four patterns people lump together

Event-driven covers several designs that behave very differently, and treating them as one thing is the most common mistake in this area. Martin Fowler separates four, and keeping them apart matters because they fail for different reasons.

  • Event notification. A service fires a lightweight event to say something changed, carrying little more than an identifier. Consumers that want details call back to the source. This gives the loosest coupling, but the flow of a whole process is hard to follow because no single place describes it.

  • Event-carried state transfer. The event carries the full new state, so consumers keep their own copy and never call back. A shipping service that receives the customer's complete new address does not have to query the customer service. The cost is duplicated data and more copies to keep in step.

  • Event sourcing. Every change is stored as an event, and current state is rebuilt by replaying the log, so the log rather than a snapshot table becomes the source of truth. It gives a complete audit trail but makes schema changes and rebuilds harder to manage.

  • CQRS. The read model and the write model are split into separate structures. It is often paired with the patterns above, and Fowler warns that it is easy to misuse and adds complexity that has to pay for itself.

The reason to name them apart is diagnostic. Fowler describes a team that blamed event sourcing for pain that actually came from CQRS and from the asynchronous messaging around it, two separate choices. If you cannot say which pattern you are running, you cannot say which one is hurting you.

What event-driven architecture costs

The decoupling is real, and so is the bill for it. Asynchronous messaging moves problems around rather than removing them.

Eventual consistency. Because consumers process at their own pace, there is a window after an event is published where services disagree about the current state. The order exists in checkout before invoicing has seen it. You have to design reads and screens to tolerate that gap, or keep synchronous calls for the flows that genuinely need strong consistency.

Harder debugging. There is no call stack spanning a business transaction that hops through five services asynchronously. When something goes wrong, the answer to which service dropped the event is rarely obvious. The usual fix is a correlation ID stamped on every event so the logs can be stitched back together, planned in from the start rather than bolted on later.

Duplicates and out-of-order arrival. Most brokers give at-least-once delivery, which means a network hiccup or a retry can deliver the same event twice. Competing consumers and retries can also process events out of the order they were emitted. Neither is a bug you can switch off; it is the normal behaviour of the transport.

The practical answer is to make consumers idempotent, so processing the same event twice has the same effect as processing it once. This is where idempotence earns its place: a processed-event table, an upsert on a natural key, or a dedupe check turns at-least-once delivery into a result that lands once. The events themselves often sit in an event log you can replay, which is what makes a clean reprocess possible after you fix a bug in a consumer.

For the harder cases there are named patterns, and it helps to know they exist as the tools you reach for. The Saga pattern coordinates a multistep process across services when there is no distributed transaction. The transactional outbox stops events going missing between a database write and the broker. A message queue buffers and retries work, and a schema registry keeps producers and consumers agreeing on the shape of an event as it changes over time.

When events are the wrong tool

Event-driven architecture is not free simplicity, and some jobs are better off without it. A request that needs an answer right now, like checking stock before confirming a basket, is easier to reason about as a plain direct call. A transaction that has to stay strongly consistent across services fights the tradeoffs above rather than benefiting from them. And the style asks for real operational maturity, because the monitoring, tracing, and recovery habits differ enough from synchronous systems that the learning curve is itself part of the cost.

Last Updated: July 10, 2026 Back to Dictionary
Keywords
event-driven architecture EDA Apache Kafka event log idempotence at-least-once delivery Eventstream pub/sub message queue event sourcing streaming data distributed systems