Skip to content
Optimal Lab

System design · interactive

Sagas and the transactional outbox

Placing an order touches four services, each with its own database. There's no transaction across all of them. A saga runs the steps one by one and, if one fails, runs compensating steps to undo the earlier ones.

Orders

Create order (pending)

Inventory

not started

Payments

not started

Shipping

not started

  1. → Orders: Create order (pending)

Publishing the OrderCreated event, with a crash

  1. BEGIN; INSERT order; INSERT outbox row; COMMIT
  2. process crashes
  3. relay reads unsent outbox rows and publishes OrderCreated
  4. The event is saved in the same transaction as the order, so it can't be lost.

Create order (pending)

What happens

Publish events

step 1 of 6

Step 1 of 4

  1. 1

    Local transactions, one per step

    Each service commits its own change. The order is visible as pending while the saga runs.

    No locks held across services.

  2. 2

    Compensate in reverse

    Each step has an undo: release stock, refund, cancel. They must be safe to retry.

    A refund isn't a rollback: the customer may see both.

  3. 3

    Orchestrated or choreographed

    An orchestrator calls each step and tracks state, or services react to each other's events. Orchestration is easier to follow.

    Temporal and Step Functions are orchestrators.

  4. 4

    Use an outbox

    Write the event to an outbox table in the same transaction as the data; a relay publishes it later. A crash can delay it, never lose it.

    Consumers must dedupe: the relay is at-least-once.

In production

Tools and technologies

Workflow engines run sagas; CDC tools relay outboxes.

Not tied to one language — use these names when you search docs or design a system.