saga-full — saga flow
Choreography, not orchestration: every service downstream of order-service reacts to
Kafka events independently — nothing tells payment-service to compensate, it just
happens to be subscribed to the same rejection event order-service reacts to. Three
steps below — the outbox pattern, the topic map, and compensation itself — are zoomed into, since
each is a real design decision worth understanding on its own.
Domain write and event-to-publish, one transaction
Every service that emits a saga event writes an OutboxRecord in the same
database transaction as its domain change — e.g. order-service creates a
PENDING Order and its matching outbox row together, or neither happens.
A separate @Scheduled poller (every 500ms, PESSIMISTIC_WRITE +
SKIP LOCKED — safe across multiple instances of the same service) ships pending
records to Kafka and deletes each one only after a confirmed send.
Four topics carry the whole saga
| Topic | Publisher | Consumer(s) |
|---|---|---|
order-created-topic | order-service | payment-service |
payment-processed-topic | payment-service | restaurant-service |
restaurant-approved-topic | restaurant-service | order-service |
restaurant-rejected-topic | restaurant-service | order-service, payment-service |
Two consumers, one rejection event
restaurant-rejected-topic has two independent consumer groups:
order-rejected-group (order-service, marks the order CANCELLED) and
payment-compensation-group (payment-service, handleOrderCompensation —
the actual compensating transaction, undoing a charge already committed because a later saga
step failed). Neither consumer knows the other exists; both just react to the same topic. This is
the concrete instance of the pattern this whole repo demonstrates.
Every consumer that mutates state guards against Kafka's at-least-once delivery with a
cheap-check-before-load idempotency pattern (existsByOrderIdAndStatus, then
findById, then a re-check after load) — a duplicate message must not double-charge
or double-allocate inventory. Per-service detail (ports, schemas, key classes):
Services Reference.