ediverse Explore the platform

Spotlight PEPPOL BIS Billing 3.0 The EU e-invoicing mandate is here — France Sept 2026, Belgium Jan 2026, Germany 2025.

Outbox (architectural)

The buffer table that turns two uncoordinated writes (DB + broker) into a single atomic write on the DB side.

Problem

Without broker XA (Kafka), how to guarantee that a business side effect (order creation) and its broker-published message both arrive or neither — without locking the application if the broker is down?

Forces

  • A broker write during a DB transaction is not transactional.
  • A broker outage must not block order intake in the application.
  • A publish retry must be idempotent on the broker side.
  • The poller must have enough throughput to not accumulate backlog.

Solution

Create an `outbox` table (id, aggregate_type, aggregate_id, payload, status, created_at). The producer inserts its message into `outbox` within the same transaction as business changes (one COMMIT). A dedicated poller — often Debezium CDC on the outbox table — reads new rows and publishes to the broker. On successful publish, status flips to `SENT`. On failure, retry with backoff. The PK or a stable message_id ensures broker-side idempotency.

EDI implementation

In EDI, Outbox is the dominant pattern to wire ERP → Kafka EDI hub. The ERP inserts the 'OrderConfirmed' event into `outbox` within the transaction that confirms the order. Debezium reads PostgreSQL WAL, publishes on `edi.orders.confirmed`. The EDI hub consumes and emits an ORDRSP to the partner. Bonus: the outbox table also serves as a message store for replay and audit — useful in case of a broker export incident.

Anti-patterns

  • Outbox without index on status / created_at — the poller scans the whole table each cycle.
  • Outbox without purge — the table grows indefinitely and the poller slows down.
  • Outbox without an idempotency message_id — a retry duplicates the message on the broker.
  • Single-threaded poller with high throughput — the poller becomes the bottleneck.

Sources