Как паттерн Event Sourcing относится к паттерну Saga?
Что проверяют
Понимаете ли вы, что паттерны отвечают на разные вопросы, не зависят друг от друга, но хорошо сочетаются. Следом спрашивают, как обеспечить атомарность «сохранил в базу и отправил событие» и что делать, если упала сама компенсация.
Как отвечать
Сначала разведите паттерны:
- Event Sourcing: способ хранить состояние. Вместо текущей строки заказа пишется журнал событий
OrderCreated,ItemAdded,OrderPaid, состояние агрегата получается свёрткой событий, для скорости есть снапшоты, для чтения строятся проекции, обычно с CQRS; - Saga: способ провести бизнес-операцию через несколько сервисов без распределённой транзакции. Операция делится на локальные транзакции, у каждого шага есть компенсация. Координация хореографией (сервисы реагируют на события друг друга) или оркестрацией (оркестратор отдаёт команды и помнит шаг).
Связь в том, что оба паттерна строятся на событиях:
- события ES питают сагу: запись
OrderCreatedв хранилище событий и есть факт, на который подписывается следующий шаг. Проблема двойной записи, которую без ES закрывают через Transactional Outbox, здесь решается сама; - состояние саги тоже можно хранить событиями: оркестратор записывает «платёж запрошен», «платёж отклонён» и после рестарта точно знает, где остановился;
- компенсации это новые события:
PaymentRefundedне стираетOrderPaid, а добавляется следом, история остаётся полной.
В Java такое собирают на Axon Framework или Eventuate, транспорт обычно Kafka. Но сагу можно сделать на обычной базе с outbox, а ES ввести в одном сервисе без саги.
Где спотыкаются
Утверждают, что одно требует другого. Большинство саг в продакшене живут поверх обычных таблиц.
Называют компенсацию откатом. Отправленное письмо не отзовёшь, списанные деньги возвращают отдельной операцией, а между шагами другие транзакции уже видят промежуточное состояние. Изоляции у саги нет, её заменяют статусами вроде PENDING и идемпотентными обработчиками.
Берут Kafka как хранилище событий для ES. В ней нет оптимистичной блокировки на уровне агрегата и удобного чтения событий одной сущности, поэтому хранилищем служит отдельная база, а Kafka только транспорт.