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_eventtable, 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 event-driven architecture, services tell each other about things that happened, and each one decides how to react. Two kinds of messages travel:
- An event 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 command 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?
An event states a fact that already happened, in the past tense, and the sender does not care who listens. reserve-stock and send-sms are commands: instructions to one receiver, which may refuse.