Home Backend

Event-Driven Architecture Explained for Backend Developers

Backend

September 23, 2026

Event-Driven Architecture Explained for Backend Developers

Place an order in an online shop and four things need to happen. The payment is taken, a confirmation email goes out, the warehouse is told to pick the item, and the customer's loyalty balance ticks up. The straightforward version of this code runs all four steps in one request handler while the customer waits. The event-driven version records a single fact — the order was placed — publishes it, and lets everything else happen elsewhere, at its own pace.

What event-driven architecture actually means

An event is a statement of fact about something that has already happened. OrderPlaced. PaymentFailed. UserEmailVerified. It is written in the past tense because it cannot be argued with: either it happened or it did not. Events are usually immutable, and that is what makes them useful. You can store them, replay them, and hand them to consumers that did not exist when the event was created.

A command is the opposite. SendWelcomeEmail is an instruction from one service to another, and the sender expects it to be carried out. Commands create coupling. Events reduce it, because a service publishes a fact to a broker and moves on. It does not know who subscribes, how many subscribers there are, or whether they are awake.

Compare that with a chain of HTTP calls, where the order service must know about the email service, wait for a response, and handle the case where it is down. Events swap a direct dependency on other services for a dependency on the broker and on a shared understanding of what OrderPlaced means.

The moving parts

Brokers

A broker receives events and holds them until consumers take them. The options are not interchangeable, and the choice shapes how the rest of the system feels.

Kafka and similar log-based systems append events to an ordered, retained log. Consumers track an offset, so they can replay history, and the same event can be read by several independent consumer groups. Retention is a setting rather than a side effect.

RabbitMQ, Amazon SQS and comparable queueing brokers push messages to consumers and remove them once acknowledged. Routing is flexible and work-distribution patterns fit naturally, but replaying yesterday's traffic is not the point. Managed options such as Amazon SNS and SQS, Google Pub/Sub and Azure Service Bus remove a lot of operational work at the cost of a little control. Match the tool to the requirement: replay and stream processing suit a log; distributing work suits a queue.

Topics, partitions and consumer groups

Events are published to a topic. To spread load, topics are split into partitions, and a consumer group lets several instances of the same service share the work. Partition by a key, such as the order ID, so events about one entity land in the same partition and stay in order. Order is guaranteed only within a partition — if two events about the same order go to different ones, nothing stops the second arriving first.

Delivery guarantees

Most brokers give at-least-once delivery by default. Duplicates are not an edge case; they are normal. Build consumers that are idempotent — recording processed event IDs, or making the underlying operation naturally repeatable — rather than hoping the broker behaves perfectly.

An order, step by step

  1. The API writes the order and an outbox record in the same database transaction. Either both land or neither does.
  2. A relay reads the outbox, publishes OrderPlaced to the broker, and marks the row as sent.
  3. The inventory consumer reserves stock and updates its own read model.
  4. The notification consumer sends the confirmation email.
  5. The loyalty consumer credits points to the customer's balance.
  6. Anything that fails is retried a few times, then parked in a dead letter queue where it raises an alert.

The outbox step matters more than it looks. Publishing directly inside a request creates a race: the event can be sent before the transaction commits, or the database write can succeed while the publish fails. The outbox pattern removes that guesswork.

Where event-driven architecture earns its keep

  • Fan-out. One event, many interested services, and adding a fourth consumer changes nothing for the publisher.
  • Spiky traffic. The broker buffers work when incoming orders outrun the systems behind them.
  • Team independence. A team can ship a new consumer without coordinating a release with the producer.
  • Audit and replay. A retained log is a record of what happened, and it can be reprocessed when a bug is found in a consumer.
  • Long-running processes. Fulfilment that spans days and several services is easier to model as a sequence of events than as one long request.

The trade-offs worth knowing before you commit

Debugging moves sideways

A single stack trace becomes a trail of events spread across services. Invest in correlation IDs and pass them through every producer and consumer, or incident response turns into guesswork.

Consistency becomes eventual

A user can see the order confirmed while the loyalty balance still shows the old number. Some of that delay is fine. Some of it needs a UI that says "processing" rather than showing something reassuring and wrong.

Duplicates and ordering

At-least-once delivery plus no global ordering means consumers must tolerate repeats and out-of-order arrivals. If a handler adds to a balance, store the event ID and check it first. If order matters, choose the partition key with care.

Schemas drift

Events are contracts. Remove a field and somebody's consumer breaks. Add one and older consumers should ignore it. Version your schemas, keep them in a registry, and treat a change as you would a change to a public API.

More infrastructure

A broker is another system to run, monitor and pay for. Consumer lag becomes a metric that matters at three in the morning, and someone has to own it.

Before you publish your first event, write down what happens if it arrives twice. If the answer is "something breaks", fix that first.

Getting started without regret

Start with one workflow, not a platform. Pick a process that is genuinely asynchronous — sending email, generating a report, syncing to a search index — and move it behind a single event. Leave the rest of the system alone. Then follow a few rules:

  • Publish from the outbox, never mid-transaction.
  • Make every consumer idempotent from day one.
  • Add a dead letter queue and alert on it before you need it.
  • Track consumer lag and treat it as an availability signal.
  • Version your event schemas and document who owns each event.
  • Test consumers against recorded real events, not hand-written fixtures.

Once that one workflow is quiet and boring, add the second. Event-driven architecture rewards restraint. The teams that struggle are usually the ones that adopted it everywhere at once and discovered the operational bill months later.

Photo: panumas nikhomkhai / Pexels

Related Posts

Developer Laptop Setup Checklist for New UK Hires
Tools

October 10, 2026

Developer Laptop Setup Checklist for New UK Hires

A practical checklist for setting up a secure, comfortable development laptop as a new UK hire, from disk encryption and access requests...

read more
A Beginner's Guide to Database Normalisation for Small Business Apps
Databases

October 09, 2026

A Beginner's Guide to Database Normalisation for Small Business Apps

A practical introduction to first, second and third normal forms, with clear examples showing how to structure small business data...

read more
How to Run Zero-Downtime Database Migrations
Databases

October 07, 2026

How to Run Zero-Downtime Database Migrations

Practical steps for changing production schemas without downtime: the expand-and-contract pattern, lock-aware statements, deploy...

read more