Outbox pattern решает проблему: как атомарно записать данные в БД и отправить событие в Kafka/RabbitMQ? Если делать это в два шага — между ними может случиться сбой, и данные рассинхронизируются.
Решение: пишешь и данные, и событие в одну БД-транзакцию. Отдельный процесс (relay) читает таблицу outbox и публикует события.
BEGIN;
INSERT INTO orders (id, amount) VALUES (1, 100);
INSERT INTO outbox (id, event_type, payload, published)
VALUES (gen_random_uuid(), 'order_created', '{...}', false);
COMMIT;
Relay периодически делает SELECT из outbox WHERE published = false, отправляет в Kafka и помечает published = true. Или используется CDC (Change Data Capture) — Debezium читает WAL PostgreSQL и отправляет изменения.
Важно: consumer должен быть идемпотентным, потому что relay может отправить событие дважды (at-least-once). Outbox — один из самых надёжных паттернов для event-driven архитектуры.