Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation
В прошлой статье мы разобрали, почему ESB и iPaaS не заменяют Commerce Orchestration. Теперь посмотрим, что именно должен делать orchestration-слой между каналами продаж, складами и учётными системами.
В предыдущих материалах я рассматривал Commerce Orchestration как отдельный слой архитектуры электронной торговли.
Условно:
Но такая схема слишком абстрактна.
Возникает совершенно закономерный вопрос:
А что именно находится внутри этого блока?
Если убрать маркетинговые формулировки, Commerce Orchestration можно свести к нескольким фундаментальным механизмам:
Причём важен не каждый механизм отдельно.
Главное — то, как они работают вместе.
Начнём с самого важного: состояния
Представим обычный заказ.
Маркетплейс прислал:
Заказ №12543 SKU: ABC-001 Количество: 2
Можно просто отправить его в ERP.
Именно так устроена большая часть простых интеграций:
Но для orchestration этого недостаточно.
Потому что между:
«мы получили заказ»
и:
«покупатель получил товар»
существует множество промежуточных состояний.
Например:
А ещё могут быть:
То есть заказ — это не просто JSON, который надо передать из системы A в систему B.
Заказ — это объект, который живёт во времени и меняет состояние.
Именно здесь начинается оркестрация.
Зачем вообще хранить состояние отдельно?
Допустим, пришёл заказ.
Orchestrator фиксирует:
Order 12543 state = NEW
Затем проверяет:
- существует ли SKU;
- доступен ли товар;
- разрешена ли продажа;
- корректна ли цена;
- существует ли нужная организация;
- доступен ли склад.
После успешной проверки:
Затем необходимо зарезервировать товар.
Получилось:
Теперь заказ можно передавать на исполнение:
Каждый переход происходит только тогда, когда выполнены определённые условия.
Это важно.
Потому что без модели состояния очень быстро возникает ситуация:
Marketplace говорит: заказ создан
ERP говорит: заказ существует
WMS говорит: товара нет
Marketplace говорит: товар уже должен ехать покупателю
Каждая система по-своему права.
А глобальное состояние заказа неизвестно.
State — это не копия состояния ERP
Здесь есть важное архитектурное отличие.
Можно сказать:
Зачем Orchestrator хранит состояние? Пусть оно хранится в ERP.
Но ERP знает состояние ERP-заказа.
Marketplace знает состояние заказа маркетплейса.
WMS знает состояние складского задания.
Логистическая система знает состояние отправления.
Например:
Маркетплейс = ПОДТВЕРЖДЕН;
ERP = СОЗДАН;
WMS = ОШИБКА;
отгрузка = НЕ СОЗДАНА.
Каково состояние торговли в целом?
Ответ:
Заказ требует внимания
Но ни одна из четырёх систем самостоятельно этого определить не может.
Именно поэтому orchestration-слою нужна собственная модель состояния.
Второй механизм — Workflow
State отвечает на вопрос:
Где мы сейчас?
Workflow отвечает:
Что делать дальше?
Простейший workflow заказа может выглядеть так:
На диаграмме всё выглядит просто.
Пока всё работает.
Реальная система выглядит немного иначе:
И вот тут появляется бизнес-логика.
Workflow — это не просто цепочка API-вызовов
Допустим:
Это интеграция.
Теперь добавим:
если stock > 0 создать резерв
если stock = 0 проверить другой склад
если другой склад доступен изменить fulfillment
если складов нет остановить заказ
если заказ крупный отправить на ручную проверку
Это уже workflow.
То есть workflow описывает не транспорт данных, а процесс принятия решений.
Третий механизм — Бизнес Правила
Workflow говорит:
Сделай проверку цены.
Бизнес правила отвечают:
Как определить, допустима ли цена?
Например:
если margin < 10% запретить автоматическую публикацию
Или:
если channel = WB и warehouse = FBO и stock < safety_stock
запретить увеличение рекламного бюджета
Или:
если товар требует сертификата и certificate.status != VALID
публикацию запретить
Или:
если order_amount > 500 000 ₽
требовать ручного подтверждения
Таким образом:
Это две разные вещи.
Почему правила лучше не зашивать в интеграции
Плохой сценарий выглядит знакомо:
Через некоторое время компания обнаруживает, что бизнес-логика размазана по десяткам интеграций.
Меняется одно правило.
Например:
нельзя продавать товар ниже маржинальности 12%.
И приходится менять:
WB Connector
Ozon Connector
Yandex Market Connector
Shop Connector
B2B Connector
В orchestration-подходе правило живёт выше:
Каналы остаются каналами.
Бизнес-логика остаётся бизнес-логикой.
А теперь происходит ошибка
До этого момента всё выглядело довольно академично.
Но распределённые системы интересны не тогда, когда всё работает.
Они интересны тогда, когда что-нибудь перестаёт работать.
Представим:
ERP получил заказ.
Создал его.
И отправил ответ:
200 OK
Но сеть оборвалась до того, как ответ дошёл до Orchestrator.
С точки зрения Orchestrator:
request = timeout
Что делать?
Логичный ответ:
retry
Повторяем:
createOrder()
ERP создаёт второй заказ.
Получаем:
Order 10001 Order 10002
Один покупатель.
Два заказа.
Вот почему следующий механизм критически важен.
Идемпотентность
Идемпотентность означает:
Повтор одной и той же операции не должен создавать новый результат, если предыдущая операция уже была выполнена.
В нашем случае Orchestrator отправляет не просто:
createOrder()
а:
createOrder( idempotency_key = marketplace:12543 )
Первый запрос:
marketplace:12543 -> ERP Order 10001
Повторный запрос:
marketplace:12543 -> уже обработан -> ERP Order 10001
Не:
ERP Order 10002
Кажется мелочью.
Но если ежедневно проходят десятки тысяч операций, отсутствие идемпотентности быстро превращается в:
- дубли заказов;
- двойное резервирование;
- двойное списание;
- повторное создание отправлений;
- неправильные остатки.
Retry тоже сложнее, чем кажется
Типичный алгоритм начинающего разработчика:
Но далеко не каждую ошибку следует повторять.
Например:
HTTP 500
возможно, временная проблема.
Retry имеет смысл.
А:
SKU_NOT_FOUND
Retry почти наверняка бесполезен.
Или:
CERTIFICATE_EXPIRED
Повтор запроса каждые 30 секунд ничего не исправит.
Поэтому нужна классификация ошибок:
Retry нельзя делать бесконечным
Пусть Ozon API недоступен.
Если у нас 100 000 операций и каждая начинает повторяться каждую секунду:
мы сами создаём дополнительную нагрузку на уже проблемную систему.
Поэтому нормальный retry обычно выглядит примерно так:
То есть используется backoff.
Часто с добавлением jitter, чтобы тысячи операций не повторились одновременно.
Но что происходит после последнего retry?
Вот это важный вопрос.
Допустим:
В плохо спроектированной интеграции ответ:
лог
То есть somewhere:
ERROR failed to update stock
И задача закончилась.
Для Orchestrator этого недостаточно.
После исчерпания retry операция должна перейти в другое состояние:
RETRY_EXHAUSTED
А workflow решить, что делать дальше.
Например:
То есть человек — тоже часть архитектуры.
Человек в контуре управления
Иногда автоматизацию ошибочно воспринимают как попытку полностью удалить человека из процесса.
На практике хороший Orchestrator должен понимать:
какие решения можно принять автоматически, а какие нельзя.
Представим ситуацию.
ERP говорит:
stock = 12
WMS говорит:
stock = 9
Marketplace сообщает:
available = 15
Какой остаток правильный?
Можно придумать автоматическое правило:
available = min(ERP, WMS)
Получим:
9
Но что, если ERP ещё не получил приход?
Или WMS задержал событие?
Или маркетплейс считает товар, уже находящийся в заказах?
Иногда система не должна угадывать.
Она должна сказать:
CONFLICT_DETECTED
И остановить потенциально опасное действие.
Это и есть Человек в контуре управления
Очень важная мысль: автоматизация должна уметь не действовать
Большинство систем проектируют вокруг идеи:
получить событие → выполнить действие.
Но для серьёзной автоматизации иногда правильное действие:
ничего не делать автоматически.
Например:
Вместо:
что-нибудь отправим, потом разберёмся
Для commerce это особенно важно, потому что ошибки быстро превращаются в деньги.
Следующий механизм — Reconciliation
Пожалуй, это один из самых недооценённых элементов интеграционной архитектуры.
Допустим, у нас событийная система.
Marketplace изменил остаток:
stock = 10
Отправил событие.
Мы его получили.
Всё отлично.
Но что произойдёт, если одно событие потерялось?
Например:
Локальная система может остаться в неправильном состоянии.
И никто этого не заметит.
Поэтому одного event-driven подхода недостаточно.
Периодически необходимо спрашивать:
А действительно ли наши системы всё ещё согласованы?
Это и есть reconciliation.
Как работает reconciliation
SKU-001 ERP stock = 100
Запрашиваем marketplace:
Marketplace stock = 94
Получаем:
delta = -6
Теперь система должна определить:
это допустимая задержка?
это продажи?
это резерв?
это потерянное событие?
это ошибка интеграции?
В простейшем случае:
EXPECTED STATE -> сравнить -> ACTUAL STATE
Если
EXPECTED == ACTUAL
ничего не происходит.
Если:
EXPECTED != ACTUAL
запускается reconciliation workflow.
Event-driven без reconciliation недостаточно
Можно построить красивую архитектуру:
И считать её надёжной.
Но события тоже:
- теряются;
- приходят дважды;
- приходят не по порядку;
- задерживаются;
- содержат старые данные;
- могут не прийти вообще.
Поэтому зрелая система обычно сочетает:
Первый механизм даёт скорость.
Второй — уверенность.
Простой пример с остатками
Допустим:
Физический остаток: 10
Получили заказ:
-1
Стало:
9
Следующий заказ:
-1
Стало:
8
Но событие второго заказа потерялось.
Orchestrator думает:
9
WMS:
8
Через reconciliation:
После чего запускается восстановление.
Без reconciliation ошибка могла жить неделями.
И вот теперь всё складывается вместе
Commerce Orchestration — это не один механизм.
Это цепочка:
Именно сочетание этих механизмов превращает интеграцию в оркестрацию.
Разберём один заказ целиком
Теперь попробуем собрать полный сценарий.
Wildberries присылает:
ORDER_CREATED
Orchestrator создаёт:
OrderState = NEW
Проверяем:
SKU существует?
Да.
NEW -> VALIDATED
Проверяем остаток:
stock = 4 order = 2
Создаём резерв:
VALIDATED -> RESERVED
Отправляем заказ в ERP:
idempotency_key = WB:ORDER:12543
ERP не отвечает.
timeout
Orchestrator делает retry.
ERP отвечает:
order already exists ERP_ORDER = 98765
Продолжаем workflow.
RESERVED -> READY_FOR_FULFILLMENT
Передаём WMS.
WMS сообщает:
reservation conflict
Orchestrator сравнивает данные:
ERP stock = 4 WMS stock = 1
Получаем:
CONFLICT
Правило говорит:
если order_qty > confirmed_stock автоматическое fulfillment запрещено
Заказ переходит:
ON_HOLD
Создаётся задача оператору.
После решения проблемы:
ON_HOLD -> READY_FOR_FULFILLMENT -> FULFILLED
Вот это уже orchestration.
ESB здесь по-прежнему может быть нужен
Важно ещё раз подчеркнуть.
Из всего описанного не следует, что:
Commerce Orchestration заменяет ESB
Нет.
ESB может прекрасно выполнять:
routing transformation protocol mediation delivery
Например:
Это нормальная архитектура.
Просто у каждого слоя своя ответственность.
ESB
Как доставить сообщение?
Commerce Orchestration
Зачем его отправлять?
Когда?
При каких условиях?
Что делать при ошибке?
Как понять, что процесс закончен?
Аналогично с iPaaS
iPaaS отлично подходит для:
получить данные -> преобразовать -> передать
Но по мере роста бизнеса сценарий постепенно превращается в:
если... -> если... -> проверить... -> повторить... -> подождать... -> если состояние... -> позвать оператора... -> сверить через 30 минут...
И в этот момент integration flow начинает играть роль бизнес-процесса.
Именно здесь и возникает архитектурная граница.
Поэтому я бы определил Commerce Orchestration так
Не:
система интеграции маркетплейсов.
Не:
ещё один ESB.
Не:
коннектор между ERP и маркетплейсами.
А:
Commerce Orchestration — это слой, который хранит состояние торговых процессов, принимает бизнес-решения и координирует выполнение операций между независимыми системами.
Его задача — превратить:
набор API
в:
управляемый бизнес-процесс
Получается следующая архитектура
Это, пожалуй, и есть главное различие между orchestration и integration.
Integration соединяет системы.
Orchestration управляет тем, что между ними происходит.
Но остаётся ещё один вопрос
До сих пор мы предполагали, что все системы физически существуют.
API может временно не отвечать.
ERP может зависнуть.
Сообщение может потеряться.
Это классические технические сбои.
Но что произойдёт, если проблема серьёзнее?
Например:
Или если часть товарного запаса физически становится недоступна?
Вот здесь обычной обработки ошибок уже недостаточно.
Возникает следующая задача:
может ли торговая система продолжить работу после потери одного из своих ключевых элементов?
И это уже переход от Commerce Orchestration к тому, что можно назвать Commerce Resilience.
Но об этом — в следующей статье.