Цель: гарантировать, что событие LowStockAlert не теряется при краше приложения после коммита БД.
Зависимость: KAFKA-1, KAFKA-2.
Проблема
Сейчас producer шлёт событие в TransactionSynchronization.afterCommit(). Если приложение падает сразу после коммита движения, но до отправки в Kafka — событие теряется навсегда; @Retryable живёт только в памяти и краш не переживает. At-least-once не гарантирован: алерт о низком остатке может пропасть.
Что сделать
- Таблица
outbox (id, тип события, payload JSON, статус/флаг отправки, created_at).
- Событие писать в
outbox в той же транзакции, что и stock_movements — атомарно с бизнес-данными.
- Отдельный релей (планировщик, polling по неотправленным) публикует в Kafka и помечает отправленным после подтверждения брокера.
- Консьюмер (KAFKA-2) делает дедуп по идемпотентному ключу события — повтор не плодит дубли в
stock_alerts.
- Стретч: явно обеспечить at-least-once + идемпотентный консьюмер; сравнить polling vs CDC (Debezium) с trade-off; продумать, чтобы два инстанса релея не отправили одно событие дважды (lock/SKIP LOCKED).
Acceptance criteria
Цель: гарантировать, что событие LowStockAlert не теряется при краше приложения после коммита БД.
Зависимость: KAFKA-1, KAFKA-2.
Проблема
Сейчас producer шлёт событие в
TransactionSynchronization.afterCommit(). Если приложение падает сразу после коммита движения, но до отправки в Kafka — событие теряется навсегда;@Retryableживёт только в памяти и краш не переживает. At-least-once не гарантирован: алерт о низком остатке может пропасть.Что сделать
outbox(id, тип события, payload JSON, статус/флаг отправки, created_at).outboxв той же транзакции, что иstock_movements— атомарно с бизнес-данными.stock_alerts.Acceptance criteria
stock_alerts(дедуп на консьюмере).