← Назад к списку
Системный дизайнBackendSenior

В микросервисной архитектуре оформление заказа затрагивает сервисы заказов, склада и платежей. Как обеспечить согласованность без распределённых транзакций?

Короткий ответ

  • 2PC в микросервисах не используют: блокировки и хрупкость
  • Сага: цепочка локальных транзакций с компенсациями при сбое
  • Оркестрация — явный координатор; хореография — реакция на события
  • Outbox гарантирует согласованную публикацию событий с записью в БД
  • Резервирование товара вместо списания — компенсируемое действие
  • Итог — согласованность в конечном счёте и идемпотентность шагов

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

Как сказать вслух

пример ответа

Классическую распределённую транзакцию с двухфазным коммитом в микросервисах не применяют — она блокирует ресурсы и хрупка к сбоям координатора. Вместо неё сага: последовательность локальных транзакций — создать заказ, зарезервировать товар, списать оплату — где у каждого шага есть компенсация. Если оплата не прошла, запускаем компенсации: снимаем резерв и отменяем заказ. События между сервисами публикую через outbox-паттерн, чтобы запись в базу и событие не разъехались. И все обработчики делаю идемпотентными, потому что доставка at-least-once.

Подробный ответ

Основной ответ

Проблема: инвариант «заказ оплачен и товар зарезервирован» размазан по трём сервисам с собственными базами. Сага разбивает процесс на локальные транзакции с компенсирующими действиями (отмена резерва, возврат платежа, отмена заказа). Два стиля: хореография — сервисы подписаны на события друг друга (OrderCreated → StockReserved → PaymentCompleted), просто для коротких цепочек, но поток логики нигде не виден целиком; оркестрация — отдельный координатор (state machine, например Temporal или самописный) ведёт шаги и компенсации явно — предпочтительна для сложных процессов, упрощает понимание и обработку таймаутов. Техническая база: transactional outbox в каждом сервисе (событие пишется в одной транзакции с данными, публикатор/CDC доставляет в брокер), идемпотентные обработчики (дедуп по event_id), таймауты шагов с переводом в компенсацию, статусная модель заказа видимая пользователю (pending → confirmed/failed). Важно проектировать компенсируемость: резервирование вместо немедленного списания склада, авторизация платежа с последующим capture.

Ключевые моменты

  • Почему не 2PC. Двухфазный коммит держит блокировки на время координации и останавливается при падении координатора — в сервисной среде это неприемлемо.
  • Компенсация — не откат. Это новое бизнес-действие (возврат денег), а не rollback: его видно в истории, оно само может сбоить и требует ретраев.
  • Оркестрация vs хореография. Хореография распределяет знание о процессе по подпискам; оркестратор держит его в одном месте — для заказа с 4+ шагами обычно выбирают его.
  • Eventual consistency наружу. Пользователь видит промежуточный статус «обрабатывается» — UX и статусная модель являются частью дизайна, а не деталью.

Практический контекст

Это центральный сеньорский вопрос про микросервисы: он связывает архитектуру, очереди и транзакционность в один сценарий. Интервьюер гоняет по сбоям: «платёжка ответила таймаутом — списали или нет?» (ответ: идемпотентный ключ платежа и запрос статуса), «компенсация упала — что дальше?» (ретраи, DLQ, ручной разбор). Сильный кандидат сам вводит outbox, идемпотентность и говорит о наблюдаемости процесса — трейсинг саги по correlation_id.

Частые ошибки

  • Предлагают двухфазный коммит или «общую транзакцию», не зная её цены в распределённой среде
  • Забывают про сбои самих компенсаций и таймауты шагов саги
  • Публикуют события после коммита без outbox — событие теряется при падении сервиса

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru