1. Courses
  2. System Design
  3. Distributed Systems
  4. Stop 4

40 minAdvanced

Events, the outbox pattern and sagas

When one action must update your database and also tell other parts of the system, there is a gap where things get lost. This lesson closes that gap with the transactional outbox, shows how to run a business process across several services with a saga, and how to make every step safe to repeat.

By the end you can

  • Tell events from commands, and say when to use each
  • Explain the dual-write problem with a concrete failure
  • Build a transactional outbox with Prisma and a relay that uses FOR UPDATE SKIP LOCKED
  • Design an order saga with compensating actions, as choreography or orchestration
  • Write an idempotent consumer with a processed_event table, and design the UI for "still confirming"

The everyday picture

At an old-style wholesale shop in a busy city market, the owner writes every sale in the ledger. When a sale needs goods from the warehouse, he writes, on the same ledger page, a small note: "Tell warehouse: 10 sacks of flour for order 5512." A runner comes by every half hour, reads the new notes, carries each message to the warehouse, and ticks the note as delivered.

If the runner is late, the message is late but not lost, because the note is on the page. If the owner tore out the page, both the sale and the note would be gone together, which is also fine: there was no sale. The only way to lose a message would be to write the sale in the ledger and keep the message in his head. That is exactly what most code does, and this lesson fixes it.

Events and commands

In an , services tell each other about things that happened, and each one decides how to react. Two kinds of messages travel:

  • An is a fact in the past tense: order.placed, payment.failed, grn.posted. The sender does not know who listens. Any number of services can react, and adding a new listener needs no change in the sender (you saw the fan-out in Queues, background jobs and pub/sub).
  • A is a request to one receiver: reserve-stock, send-sms, charge-payment. It can be refused, and the sender usually wants an answer.

Use events to keep services loosely connected ("an order was placed; whoever cares, react"). Use commands when one step must tell another specific service to act. Both have the same delivery problem, so everything below applies to both.

Quick check

Which of these messages is an event?

Loading the rest of the lesson…