События и очереди

6 мин чтения

Saga: как провести один бизнес-процесс через несколько сервисов без общей транзакции

В распределённой системе заказ, оплата, резерв товара и доставка могут жить в разных сервисах. Saga помогает связать их в один процесс — но вместо простого rollback приходится проектировать бизнес-компенсации.

В монолите многие операции можно провести в одной транзакции: изменили несколько таблиц, что-то пошло не так — откатили всё назад.

В микросервисной системе тот же бизнес-процесс может проходить через несколько независимых сервисов и баз. Заказ создан. Деньги списаны. Товар зарезервирован. Доставка ещё не оформлена. И именно в этот момент один шаг падает.

Обычного «откатить транзакцию» уже нет. Нужно решить, что делать с уже выполненными действиями. Здесь появляется Saga.

Какую проблему мы решаем

Saga разбивает длинный бизнес-процесс на последовательность локальных операций. Каждый сервис выполняет свою часть и сообщает результат.

Если все шаги успешны, процесс завершается. Если один шаг не удался, запускаются компенсирующие действия для тех операций, которые уже произошли.

Например, если деньги уже списаны, а товар зарезервировать не удалось, компенсацией может быть возврат платежа. Это не технический rollback базы. Это отдельное бизнес-действие.

Что получает бизнес

Бизнес получает возможность строить сквозные процессы поверх независимых систем, не объединяя их обратно в один технический монолит.

Команды могут отдельно развивать оплату, заказы, логистику или другие домены, а компания всё равно сохраняет единый клиентский сценарий.

Это особенно ценно, когда разные части процесса принадлежат разным командам, масштабируются отдельно или даже используют внешних поставщиков.

Есть и организационный эффект: правила нештатных ситуаций становятся явными. Что делать, если деньги списались, а заказ не создался? Как долго ждать подтверждение? Когда возвращать деньги? Кто отвечает за зависшую операцию?

Saga заставляет превратить эти вопросы из «аварийных исключений» в часть бизнес-процесса.

Что получает команда

Команды сохраняют локальные транзакции внутри своих сервисов и не зависят от общей распределённой транзакции через все системы.

Saga может координироваться централизованным оркестратором либо строиться через события, когда сервисы реагируют друг на друга.

Но инженерная сложность заметно растёт. Нужно хранить состояние процесса, обрабатывать повторные сообщения, обеспечивать идемпотентность, уметь продолжать процесс после сбоя и понимать, на каком шаге всё остановилось.

Что получает клиент

Клиент получает возможность завершать сложный сценарий через несколько систем как единый продуктовый процесс.

Но он может увидеть промежуточные состояния: «заказ обрабатывается», «возврат инициирован», «ожидаем подтверждение склада». В распределённой системе не всё обязано становиться окончательным мгновенно.

Поэтому важна не только техническая корректность, но и понятная коммуникация состояния процесса.

Чем мы за это платим

Самая большая цена — компенсация не всегда равна отмене.

Отправленное письмо нельзя «не отправить». Начатую физическую доставку нельзя откатить как запись в базе. Возврат денег сам может занять время или завершиться ошибкой.

Нужно проектировать последствия каждого шага и понимать, что часть процессов после сбоя потребует ручного вмешательства.

Кроме того, бизнес должен принять eventual consistency: некоторое время разные сервисы могут показывать разные стадии одной операции.

Когда Saga не нужна

Если весь процесс живёт в одном приложении и одной базе, обычная транзакция почти всегда проще и надёжнее.

Не стоит создавать распределённый workflow только ради архитектурной красоты. Saga оправдана тогда, когда границы сервисов уже нужны по другим причинам, а сквозной процесс должен эти границы пересекать.

Что стоит спросить перед решением

В итоге

Saga позволяет сохранить независимость систем и при этом провести через них один бизнес-процесс.

Цена этой независимости — необходимость заранее договориться не только о happy path, но и о том, что именно бизнес делает, когда процесс остановился посередине.