Skip to content

Architecture of nats-outbox

The architecture of nats-outbox relies on a strict separation of concerns between transactional writing and asynchronous delivery.

Overview

The system is composed of three main building blocks: 1. The Application (Sender): your business logic that modifies state and creates events. 2. PostgreSQL (Database): the single source of truth ensuring local atomicity (ACID). 3. The Relay (Message Relay): a separate process responsible for extracting events from the database and delivering them to the broker.

sequenceDiagram
    participant App as Application (FastAPI)
    participant DB as PostgreSQL
    participant Relay as nats-outbox Relay
    participant NATS as NATS JetStream

    Note over App,DB: 1. Atomic Transaction (outbox_transaction)
    App->>DB: BEGIN
    App->>DB: INSERT INTO domain_table
    App->>DB: INSERT INTO outbox_events (status='pending')
    App->>DB: COMMIT

    Note over DB,NATS: 2. Asynchronous Delivery
    loop Polling (every second)
        Relay->>DB: SELECT FOR UPDATE SKIP LOCKED
        DB-->>Relay: row (status='pending')
        Relay->>NATS: Publish (Nats-Msg-Id=event_id)
        NATS-->>Relay: ACK
        Relay->>DB: UPDATE status='published'
    end

The role of each component

1. The Context Manager (outbox_transaction)

This is the API facade used in your business code. Its role is to intercept tx.publish_event() calls, transform these calls into SQLAlchemy objects (OutboxEvent), and insert them into the current session before triggering a single SQL COMMIT.

2. The Tables (Outbox and Inbox)

The outbox_events table serves as a temporary transactional queue. The inbox_events table (optional) is used for the Inbox pattern on the consumer side, ensuring idempotent processing.

3. The Relay Process

This is the background worker. It operates as a process completely isolated from your Web API. - It connects independently to PostgreSQL and NATS. - It uses a non-blocking pessimistic locking mechanism (SKIP LOCKED) to ensure no other concurrent relay attempts to send the same event simultaneously. - In the event of network loss with NATS (timeout or missing ACK), the Relay does not mark the event as published.