Ответ
Паттерн Outbox (или Transactional Outbox) решает проблему надежной отправки сообщений/событий в рамках распределенной транзакции между базой данных и брокером сообщений (например, Kafka, RabbitMQ).
Задача: В микросервисной архитектуре часто нужно обновить базу данных И отправить событие о этом изменении. Если сделать это последовательно, возможна ситуация, когда:
- Данные в БД сохранены.
- Отправка события в брокер завершилась ошибкой. В результате система становится несогласованной.
Решение Outbox:
- В рамках одной транзакции с основным бизнес-действием приложение записывает событие в специальную таблицу
outboxв той же БД. - После коммита транзакции отдельный процесс-реле (
Message Relay) забирает новые записи изoutboxи публикует их в брокер сообщений. - После успешной публикации запись из
outboxудаляется или помечается как отправленная.
Пример структуры таблицы outbox:
CREATE TABLE outbox (
id BIGSERIAL PRIMARY KEY,
aggregate_id UUID NOT NULL, -- ID связанной сущности
event_type VARCHAR(255) NOT NULL, -- e.g., "UserCreated"
payload JSONB NOT NULL, -- Данные события
created_at TIMESTAMP NOT NULL,
sent_at TIMESTAMP NULL -- Отметка об успешной отправке
);
Этот паттерн гарантирует, что каждое событие будет доставлено как минимум один раз (at-least-once delivery) и является ключевым для реализации надежной асинхронной коммуникации между сервисами.