Get Started

Design an event-driven system with a message broker

Stop making your services phone each other. Put a message broker in the middle: one service announces that something happened, and any number of others react, on their own schedule. By the end you have a producer and several consumers talking through Redis Streams in Docker, with events fanned out to independent subscribers, a group of workers sharing the load, acknowledgements, and a dead worker's events redelivered to a live one.

79 minAdvancedTools: Free. The broker is an open-source Redis container that runs on your own PC. No GPU, no cloud account.Step 1 free
Tested 12 Oct 2026 · Windows 11, Docker Engine 29.1.3 on WSL2 Ubuntu, Redis 7.4.1 (redis:7.4.1-alpine), Python 3.13.5 and redis-py 8.1.0. Every {out} block below is copied from these scripts running against that broker.
A running message broker in Docker, and a producer plus several consumers that communicate only through events, never by calling each other
Real proof of the four behaviours that define pub/subfan-out to independent subscribers, a consumer group sharing load, acknowledgement, and redelivery when a consumer dies before acknowledging
A clear command of the vocabulary (event, broker, stream, publisher, subscriber, consumer group, acknowledgement, at-least-once, ordering) and when event-driven beats request/response
  • A starter kit: the broker's docker-compose.yml, a producer, two kinds of consumer, a recovery script and an inspector, all runnable with one dependency
Before you start
  • Docker, running. If containers are new to you, do Docker without fear first: this mission uses one small Redis container and the same docker compose up -d routine.
  • Python and uv from Level 0 (Install anything from GitHub, safely has the one-line uv install). Check with uv --version. The only package you add is redis.
  • The terminal basics, and the willingness to keep four or five terminal windows open at once: a broker, a producer and a few consumers. New to the terminal? Do the Terminal survival kit first.
  • A little Python (functions, loops, dicts). You do not need to know Redis; every command is explained where it first appears.
01/07Why events instead of direct calls0 done
STEP 01 of 07~8 min

Why events instead of direct calls

Before any code, the shift in thinking. Direct calls couple services together and fail together. Events let one service announce a fact and walk away, while others react in their own time.

  1. Picture an online shop. A customer pays, and now the shop must take payment, email a receipt, reserve stock, and tell the analytics team. The obvious way is for the checkout service to call each of the others in turn: POST /payments, then POST /email, then POST /stock, then POST /analytics. This is request/response, the style you already know from building your own API.

  2. It has three problems that grow with the system:

    • Coupling. Checkout has to know every service that cares about a sale, their addresses, and their request formats. Add a loyalty-points service next month and you edit, test and redeploy checkout.
    • Shared fate. If the email service is down or slow, the customer's payment hangs or fails, over something that should not block the sale. One sick service infects the caller.
    • No replay. The call happens once. If analytics was restarting at that instant, the fact of the sale is gone. Nobody kept a record that anyone can read later.
  3. Event-driven architecture turns the arrows around. Checkout does one thing: it records that an order was placed and publishes that fact to a broker, a piece of middleware whose whole job is to carry messages between programs. Checkout does not know who listens. Payment, email, stock and analytics each subscribe to the fact and react when they are ready. The vocabulary, once, so the rest of the lesson reads cleanly:

    • Event: a record that something already happened, in the past tense, like order.placed. It is a fact, not a command. The publisher is not telling anyone what to do.
    • Broker: the server in the middle that stores events and hands them to whoever asked for them. Redis, used here, is one. Kafka, RabbitMQ, NATS and cloud services like SQS/SNS are others.
    • Stream (also called a topic or subject): the named channel events are published to. Here it is events:orders. A broker can carry many streams at once.
    • Publisher (producer): the program that appends events to a stream. Subscriber (consumer): a program that reads them.
    • Event-driven architecture: a design where services communicate by publishing and reacting to events through a broker, instead of calling each other directly.
    TipThis is not the same as a background job queue. A task queue exists so one producer can hand *work* to workers: "resize this image", and exactly one worker does it. Pub/sub exists so one producer can announce a *fact* to many independent readers, each of whom does something different with it. The difference, and where they overlap, becomes concrete in steps 4 and 5.
  4. Events do not replace request/response everywhere. When the caller needs an answer right now (is this card valid? what is this user's balance?), a direct call is right. Events shine when the answer is "something happened, and several parties care, and none of them needs to block the others".

✓
CheckpointYou can state, in one sentence, why publishing an order.placed event is more robust than the checkout service calling the email service directly.
If it breaksIsn't this just more moving parts for the same result? + 1 more
Isn't this just more moving parts for the same result?
For two services, yes, a direct call is simpler. The broker earns its place once a third, fourth or fifth service wants the same fact, or when you cannot afford the caller to fail because a reader is down. Below about three services, keep it simple.
Where does the event actually live?
In the broker, on its disk. That is the point: the fact outlives the moment it happened, so a consumer that was offline can read it when it comes back. You will see this when you publish before any consumer is running.
Still stuck? Ask the community
Next mission · Advanced buildsBuild a Dropbox-style file sync engine
→