Как только бизнес-операция расходится на несколько сервисов — оформить заказ = списать оплату, зарезервировать товар, создать доставку, уведомить клиента — встаёт вопрос: кто дирижирует этой последовательностью? Есть ровно два ответа, и выбор между ними определяет, будет ли система понятной через год. Хореография — дирижёра нет: каждый сервис реагирует на события и публикует свои, поток складывается сам. Оркестрация — есть центральный координатор, который явно говорит, что делать дальше. Технологически оба реализуемы; разница глубже — в том, где живёт знание о процессе: размазано по всем участникам или собрано в одном месте.
Часть серии «Событийные паттерны». Тесно связана с Saga (у которой есть обе разновидности) и Temporal (движок оркестрации). Эта статья — про сам выбор координации и его цену.
В статье
- Два способа координации
- Где живёт знание о процессе
- Хореография: за и против
- Оркестрация: за и против
- Сравнение по осям
- Антипаттерны: event-спагетти и god-оркестратор
- Как это ложится на Saga и Temporal
- Лунная база: аварийный протокол двумя способами
- Когда что и почему гибрид
- Checklist: хореография или оркестрация
Два способа координации
Возьмём «оформить заказ»: оплата → резерв товара → доставка → уведомление. Один и тот же процесс собирается принципиально по-разному.
Хореография — сервисы подписаны на события, реагируют и публикуют свои. Единого сценария нет нигде, поток эмерджентный:
Заказ ──publish── OrderPlaced
Оплата ──on OrderPlaced──→ списать ──publish── PaymentTaken
Склад ──on PaymentTaken──→ зарезервировать ──publish── StockReserved
Доставка ──on StockReserved──→ создать ──publish── ShipmentCreated
Уведомл. ──on ShipmentCreated──→ отправить письмо
Оркестрация — центральный координатор (saga-оркестратор, Temporal-workflow) явно вызывает шаги по порядку и решает, что дальше:
OrderOrchestrator:
take_payment() → ok
reserve_stock() → ok
create_shipment() → ok
notify_customer() → ok
(при ошибке любого шага — явная компенсация предыдущих)
Технологически оба реальны: хореография — это чистый pub/sub на Kafka/NATS; оркестрация — движок вроде Temporal или Camunda. Но выбор между ними — не про технологию.
Где живёт знание о процессе
Единственный по-настоящему важный вопрос: где записано знание о том, как устроен весь процесс?
- Хореография: знание размазано. Каждый сервис знает только «на какое событие я реагирую и какое публикую». Целиком последовательность «оплата → резерв → доставка → уведомление» не описана нигде — она существует только как эмерджентное поведение системы. «Весь поток» не видит никто.
- Оркестрация: знание собрано в координаторе. Процесс читается как сценарий в одном файле: вот шаги, вот порядок, вот что при ошибке. Есть одно место, куда смотреть и что менять.
Из этого единственного различия вырастают все дальнейшие компромиссы. Оно же связано с законом Конвея и владением: размазанный процесс хорошо ложится на автономные команды, собранный — требует владельца процесса.
Хореография: за и против
За:
- слабая связанность — сервисы не знают друг о друге, только о событиях; publisher не в курсе, кто и сколько его слушает;
- автономность — команда меняет свой сервис, не согласуя с «дирижёром»;
- лёгкое расширение — добавить нового потребителя события = подписаться, не трогая существующих (новый сервис аналитики на
OrderPlaced— и всё); - нет центрального SPOF/бутылочного горлышка — координатора, который может лечь, просто нет.
Против:
- непрослеживаемость — «никто не знает весь поток»; чтобы понять, что происходит после
OrderPlaced, надо обойти все сервисы и посмотреть, кто на что подписан; - тяжёлая отладка/мониторинг — вопрос «на каком шаге застрял заказ 42» не имеет одного места для ответа;
- риск циклических зависимостей — событие A триггерит B, B публикует C, C снова триггерит A; в хореографии это легко не заметить;
- размазанные компенсации — откат при ошибке (вернуть деньги, снять резерв) тоже приходится собирать из реакций разных сервисов;
- трудно менять поток — вставить шаг в середину значит переподписать события у нескольких сервисов.
Оркестрация: за и против
За:
- явный поток — вся последовательность в одном месте, читается как код, меняется в одном файле;
- простой мониторинг/отладка — статус процесса виден в координаторе: «заказ 42 на шаге create_shipment, ретрай 2»;
- централизованная обработка ошибок и компенсаций — откат описан там же, где прямой ход (прямой мостик к Saga);
- проще эволюция сложного процесса — вставить/переставить шаг = правка в одном сценарии.
Против:
- связанность с координатором — он «знает» про все сервисы, которые вызывает; это осознанная централизация знания;
- риск бутылочного горлышка/SPOF — координатор на критическом пути (смягчается durable-движками вроде Temporal, которые переживают падения);
- соблазн разрастись — координатору легко начать тянуть в себя бизнес-логику сервисов (см. god-оркестратор ниже).
Сравнение по осям
| Ось | Хореография | Оркестрация |
|---|---|---|
| Знание о процессе | размазано по сервисам | собрано в координаторе |
| Связанность | слабая (только события) | координатор связан со всеми |
| «Весь поток» виден | нигде | в одном месте |
| Мониторинг/отладка | тяжело (обход сервисов) | легко (статус в координаторе) |
| Добавить потребителя | тривиально (подписка) | правка сценария |
| Изменить порядок шагов | тяжело (переподписка) | легко (один файл) |
| Компенсации | размазаны | централизованы |
| SPOF | нет центрального | координатор (смягчается durable) |
| Циклы/спагетти | легко получить незаметно | видны в сценарии |
Ни одна колонка не выигрывает целиком — это компромисс между автономностью и наблюдаемостью, а не «лучше/хуже».
Антипаттерны: event-спагетти и god-оркестратор
Каждый подход, доведённый до крайности, вырождается в свой антипаттерн.
Event-спагетти (хореография ушла вразнос): события триггерят события, которые триггерят события, и цепочку никто не может проследить. Отладка превращается в археологию — «а кто вообще опубликовал это событие и почему заказ обработался дважды?». Признак: чтобы понять один бизнес-процесс, разработчик открывает пять репозиториев и рисует граф подписок на бумаге.
God-оркестратор (оркестрация ушла вразнос): координатор перестаёт только координировать и начинает тянуть в себя бизнес-логику всех сервисов — валидацию, расчёты, правила. Он становится распределённым монолитом и единой точкой связности: любое изменение в любом сервисе требует правки оркестратора. Признак: оркестратор знает про домены, которые его не касаются, и растёт быстрее, чем сами сервисы.
Граница проста: оркестратор решает что и в каком порядке делать; как делать — остаётся внутри сервисов.
Как это ложится на Saga и Temporal
Этот выбор — не абстракция, он живёт внутри знакомых паттернов.
- Saga бывает двух разновидностей: choreography-based saga (сервисы реагируют на события саги, компенсации размазаны) и orchestration-based saga (оркестратор ведёт сагу и её компенсации). Это ровно тот же выбор — внутри одного паттерна распределённой транзакции. Практическую сторону обеих разбирает Saga на практикеготовится, с 18 августа.
- Движки оркестрации: Temporal, Camunda/Zeebe — их сама суть в централизации знания о процессе (workflow как код). Чистый pub/sub на Kafka/NATS — это хореография по умолчанию.
- Идемпотентность нужна обеим: и в хореографии, и в оркестрации шаги ретраятся и события задваиваются — консьюмеры обязаны быть идемпотентными (см. гарантии доставки и идемпотентность).
Лунная база: аварийный протокол двумя способами
Разгерметизация модуля запускает протокол: оценить критичность → зарезервировать аварийный кислород → переключить энергоснабжение → отправить робота → уведомить оператора.
Хореографией: датчик публикует DepressurizationDetected; кислородная подсистема реагирует и публикует EmergencyOxygenReserved; энергоузел реагирует на него и публикует PowerRerouted; и так далее. Плюс — подсистемы автономны, добавить новую реакцию (например, аварийное освещение на DepressurizationDetected) можно, не трогая остальные. Минус — когда протокол «завис», непонятно, на каком шаге: знание о полной цепочке не собрано нигде, а в аварийном сценарии это дорого.
Оркестрацией: EmergencyOrchestrator явно ведёт шаги, ждёт подтверждения каждого с таймаутом, при провале — эскалация на оператора, при отказе — компенсация. Весь протокол читается в одном месте, статус («шаг 3, ждём подтверждения энергоузла, таймаут 5 мин») виден сразу. Для критичного процесса с компенсациями и человеком в цикле это перевешивает — поэтому аварийный протокол оркестрируют, а не пускают на самотёк реакций.
Разный характер процесса → разный выбор координации. Это подводит к главному правилу.
Когда что и почему гибрид
Выбирай хореографию, когда: поток простой и коротких шагов, важна автономность команд, идёт fan-out реакций (одно событие → много независимых потребителей), приоритет — слабая связанность, а сквозная наблюдаемость не критична.
Выбирай оркестрацию, когда: процесс многошаговый с компенсациями, нужна видимость и контроль хода, есть таймауты и человек в цикле, длинные процессы (часы/дни) — здесь durable-движок вроде Temporal снимает и SPOF, и ручной state machine.
Гибрид — это реальность, а не компромисс от бессилия. Зрелые системы оркестрируют критичную бизнес-транзакцию (оплата заказа, аварийный протокол) и хореографируют периферию вокруг неё (уведомления, аналитика, аудит, индексация). Ключевое: выбор делается на процесс, а не на всю систему. Один и тот же сервис может быть шагом оркестрированной саги и одновременно публиковать события, на которые независимо реагирует аналитика. Как это соотносится с остальными событийными паттернами — в матрице решенийготовится, с 8 августа.
Checklist: хореография или оркестрация
- Нужно ли видеть «весь поток» в одном месте и отвечать на «на каком шаге застряло»? → скорее оркестрация.
- Есть ли компенсации/откат при частичном сбое? → скорее оркестрация (централизованные компенсации).
- Процесс длинный, с таймаутами и человеком в цикле? → оркестрация на durable-движке.
- Приоритет — автономность команд и лёгкое добавление потребителей? → скорее хореография.
- Это fan-out (одно событие → много независимых реакций)? → хореография.
- Рискуете ли вы уже сейчас получить event-спагетти (цепочки, которые никто не проследит)? → добавьте оркестрацию в ядро.
- Не разрастается ли координатор в god-оркестратор (тянет бизнес-логику)? → верните логику в сервисы.
- Решение принято на конкретный процесс, а не на всю систему разом?
Если по критичному процессу большинство ответов — «нужна видимость и компенсации», оркестрируйте его, а периферию оставьте на хореографии.
Демо и версии
- Demo:
digital-cookbook/architecture/event-coordination— один сценарий «оформить заказ» двумя способами: (1) хореография через in-process event-bus — сервисы реагируют на события и публикуют новые, без внешнего брокера (стенд намеренно самодостаточен: в реальной системе на этом месте были бы Kafka/NATS); (2) оркестрация через координатор (Temporal-workflow). Наглядно: где виден «весь поток», как отлаживать «застрявший» заказ, как добавить шаг в каждом варианте, как выглядит компенсация при сбое. - Версии движка (Temporal) и зависимостей пиновать при написании; при переносе хореографии на реальный брокер (Kafka/NATS) пиновать и его.
Документация и первоисточники
- Канон: microservices.io — Saga (choreography vs orchestration) (Chris Richardson), Sam Newman — «Building Microservices» (глава про оркестрацию и хореографию), Martin Fowler — What do you mean by “Event-Driven”?.
- Движки оркестрации: Temporal, Camunda.
- Смежное на сайте: Saga, Saga на практикеготовится, с 18 августа, Temporal (durable workflows), событийная архитектура — карта, гарантии доставки и идемпотентность, Outbox/Inbox, матрица решений по событийным паттернамготовится, с 8 августа, карта выбора брокеров.
Комментарии