Architecture

The Outbox Pattern for Heterogeneous Systems

A modern, practical approach to handling dual writes when one business action must propagate across independent systems.

Cover illustration for the outbox pattern article

Distributed systems often need one business action to update multiple independent systems. That is where reliability breaks most often.

The root issue is simple: these systems do not share one atomic transaction boundary.

Problem

The general dual-write problem

A single action may need to propagate to heterogeneous targets such as:

  • an internal database
  • a partner database
  • a blockchain transaction flow
  • an event publish pipeline

If one operation succeeds and another fails, state diverges. This is not a finance-specific issue. It is a general distributed systems problem.

Pattern

What the Outbox Pattern does

The Outbox Pattern makes one local system authoritative for the state change, and records external propagation tasks durably in the same local commit.

Instead of writing directly to every external target during the request, you commit local business state and outbox records in one transaction, then let background jobs process those outbox records reliably.

Example

Token withdrawal flow

This use case starts when the user confirms a withdrawal amount and destination address. From this action, the platform must coordinate updates across partner systems, blockchain execution, and event propagation without losing consistency.

Outbox-focused tables

withdrawals is the business source of truth.

  • id (UUID), user_id
  • asset, network, amount
  • destination_address
  • status (REQUESTED, BROADCASTED, CONFIRMED, FAILED)
  • tx_hash, failure_reason
  • requested_at, broadcasted_at, confirmed_at
  • created_at, updated_at

outbox_events is the durable integration queue.

  • id (UUID)
  • aggregate_type (WITHDRAWAL)
  • aggregate_id (references withdrawals.id)
  • event_type (for example WITHDRAWAL_REQUESTED, PARTNER_SYNC_REQUESTED, BLOCKCHAIN_BROADCAST_REQUESTED, WITHDRAWAL_CONFIRMED)
  • payload (JSON), idempotency_key
  • status (PENDING, PROCESSING, DONE, RETRYING, FAILED)
  • attempt_count, next_retry_at, last_error
  • created_at, updated_at

Jobs (no code)

OutboxDispatcherJob

  • Pulls PENDING events
  • Marks them PROCESSING
  • Routes by event_type to partner sync, blockchain broadcast, or event publish

OutboxRetryJob

  • Handles transient failures
  • Moves event to RETRYING
  • Increments attempt_count and schedules next_retry_at

OutboxFailureJob

  • Marks event as FAILED when retry limit is reached or error is non-retryable
  • Triggers alerting and investigation workflow

WithdrawalConfirmationJob

  • Tracks on-chain confirmation after broadcast
  • Updates withdrawals.status to CONFIRMED
  • Inserts follow-up outbox event for downstream propagation

Operations

Status semantics and reliability

  • RETRYING: last attempt failed, automatic retry is scheduled
  • FAILED: terminal state, no more automatic retries

With this design, failures become visible and actionable instead of silent data divergence.

Conclusion

A practical pattern for consistency

The Outbox Pattern is a practical reliability pattern for heterogeneous architectures. When a business action must propagate across systems that cannot commit atomically together, Outbox keeps state and side effects aligned.