Back to path
LargePortfolio centerpiece ~29h· 5 milestones

Build an event-driven service that scales and recovers

The synchronous version melts under spikes and loses work when something downstream fails. You re-architect it around a queue with the patterns that make async systems trustworthy.

Event-driven architectureMessage queuesOutbox patternIdempotent consumersRetries & DLQLoad testingObservability/SLOsCost modelingChaos testingBlameless postmortem

What you'll build

An event-driven service with a message queue, the transactional outbox pattern, idempotent consumers, retries with a dead-letter queue, and a load test proving it scales horizontally.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

docker-compose.ymlyaml
services:
  rabbitmq:
    image: rabbitmq:3-management
    ports: ["5672:5672", "15672:15672"]   # 15672 = management UI
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: events
    ports: ["5432:5432"]

Reading this file

  • image: rabbitmq:3-managementThe management image bundles a web UI so you can watch messages flow through queues as you build.
  • ports: ["5672:5672", "15672:15672"]5672 is the broker port your app uses, 15672 is the management dashboard in your browser.
  • image: postgres:16Postgres runs beside the broker because the outbox pattern stores events in the same database.

Broker + DB locally; the management UI lets you watch the queue.

That's 1 of 9 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Split sync from async

    5 guided steps

    Doing slow or failure-prone work inside the request is what makes a service buckle under spikes. Moving it behind a queue decouples request latency from downstream availability.

  2. 2

    Don’t lose writes

    5 guided steps

    Writing to the DB and publishing an event are two separate systems, if one succeeds and the other fails, your data and your events disagree forever. The outbox pattern makes them atomic.

  3. 3

    Make consumers safe

    5 guided steps

    At-least-once delivery means consumers will see duplicates and will hit messages they can never process. Idempotency plus a DLQ is what keeps the system correct and unblocked.

  4. 4

    Scale, observe & cost it

    6 guided steps

    The promise of an event-driven design is horizontal scale, but you have to prove throughput actually rises with consumers, watch for backlog, and know what each event costs. Observability and a cost model are how you operate it without surprises.

  5. 5

    Run chaos experiments & write the postmortem

    6 guided steps

    A trustworthy async system is one you have deliberately broken and watched recover. Structured chaos experiments plus a blameless postmortem are what turn "should work" into "here is the evidence, and here is what we will harden."

What's inside when you start

4 starter files, ready to clone
5 guided milestones
5 full reference solutions
9 code blocks explained line-by-line
5 "is it working?" checks
4 interview questions it prepares you for

You'll walk away with

A service repo demonstrating the outbox + DLQ patterns
A load-test report showing throughput scaling with consumer count, plus a $/1M-events cost model naming the dominant driver
A design doc on delivery guarantees and failure handling, with recovery evidence
A chaos-experiment report (hypotheses + results) and a blameless postmortem with action items

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building