sydepsystem design atlas

Transactional Outbox

Write state and the event to publish in the same local transaction.

IntermediateMessagingData
Order ServiceComputeDB + Outbox TableStorageOutbox RelayComputeEvent StreamMessaging

The service writes its state change and an outbox row in one database transaction, then a relay polls the outbox and publishes to the stream. This avoids the dual-write problem of updating a database and a broker separately.

When to use it

  • A state change must reliably produce an event, with no gap where one succeeds and the other fails
  • You cannot use distributed transactions across the database and the broker

Trade-offs

  • Adds relay infrastructure and polling latency
  • Requires idempotent consumers, since the relay can redeliver

Components used

Managed App ServiceRelational DatabaseWorkflow OrchestratorEvent Stream

How it works

  • Rather than writing to the database and then publishing to a broker, the service writes the business row and an outbox row in a single local transaction.
  • A separate relay polls or tails the outbox table and publishes those rows to the broker, marking them sent.
  • Because both writes share one transaction, it is impossible to persist state without also recording the event.

Used in the wild

  • Any service that must reliably emit events reflecting its own state changes.
  • Microservices replacing dual writes that occasionally drift out of sync.
  • Saga participants that must announce each completed step.

Good to know

  • It solves the dual-write problem, which is really the absence of distributed transactions between a database and a message broker.
  • The relay guarantees at-least-once delivery — a crash after publishing but before marking sent will republish. Consumers still need idempotency; the outbox only removes lost messages, not duplicates.