Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

В прошлой статье мы разобрали, почему ESB и iPaaS не заменяют Commerce Orchestration. Теперь посмотрим, что именно должен делать orchestration-слой между каналами продаж, складами и учётными системами.

В предыдущих материалах я рассматривал Commerce Orchestration как отдельный слой архитектуры электронной торговли.

Условно:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Но такая схема слишком абстрактна.

Возникает совершенно закономерный вопрос:

А что именно находится внутри этого блока?

Если убрать маркетинговые формулировки, Commerce Orchestration можно свести к нескольким фундаментальным механизмам:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Причём важен не каждый механизм отдельно.

Главное — то, как они работают вместе.

Начнём с самого важного: состояния

Представим обычный заказ.

Маркетплейс прислал:

Заказ №12543 SKU: ABC-001 Количество: 2

Можно просто отправить его в ERP.

Именно так устроена большая часть простых интеграций:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Но для orchestration этого недостаточно.

Потому что между:

«мы получили заказ»

и:

«покупатель получил товар»

существует множество промежуточных состояний.

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

А ещё могут быть:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

То есть заказ — это не просто JSON, который надо передать из системы A в систему B.

Заказ — это объект, который живёт во времени и меняет состояние.

Именно здесь начинается оркестрация.

Зачем вообще хранить состояние отдельно?

Допустим, пришёл заказ.

Orchestrator фиксирует:

Order 12543 state = NEW

Затем проверяет:

  • существует ли SKU;
  • доступен ли товар;
  • разрешена ли продажа;
  • корректна ли цена;
  • существует ли нужная организация;
  • доступен ли склад.

После успешной проверки:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Затем необходимо зарезервировать товар.

Получилось:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Теперь заказ можно передавать на исполнение:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

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

Это важно.

Потому что без модели состояния очень быстро возникает ситуация:

Marketplace говорит: заказ создан

ERP говорит: заказ существует

WMS говорит: товара нет

Marketplace говорит: товар уже должен ехать покупателю

Каждая система по-своему права.

А глобальное состояние заказа неизвестно.

State — это не копия состояния ERP

Здесь есть важное архитектурное отличие.

Можно сказать:

Зачем Orchestrator хранит состояние? Пусть оно хранится в ERP.

Но ERP знает состояние ERP-заказа.

Marketplace знает состояние заказа маркетплейса.

WMS знает состояние складского задания.

Логистическая система знает состояние отправления.

Например:

Маркетплейс = ПОДТВЕРЖДЕН;

ERP = СОЗДАН;

WMS = ОШИБКА;

отгрузка = НЕ СОЗДАНА.

Каково состояние торговли в целом?

Ответ:

Заказ требует внимания

Но ни одна из четырёх систем самостоятельно этого определить не может.

Именно поэтому orchestration-слою нужна собственная модель состояния.

Второй механизм — Workflow

State отвечает на вопрос:

Где мы сейчас?

Workflow отвечает:

Что делать дальше?

Простейший workflow заказа может выглядеть так:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

На диаграмме всё выглядит просто.

Пока всё работает.

Реальная система выглядит немного иначе:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

И вот тут появляется бизнес-логика.

Workflow — это не просто цепочка API-вызовов

Допустим:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Это интеграция.

Теперь добавим:

если stock > 0 создать резерв

если stock = 0 проверить другой склад

если другой склад доступен изменить fulfillment

если складов нет остановить заказ

если заказ крупный отправить на ручную проверку

Это уже workflow.

То есть workflow описывает не транспорт данных, а процесс принятия решений.

Третий механизм — Бизнес Правила

Workflow говорит:

Сделай проверку цены.

Бизнес правила отвечают:

Как определить, допустима ли цена?

Например:

если margin < 10% запретить автоматическую публикацию

Или:

если channel = WB и warehouse = FBO и stock < safety_stock

запретить увеличение рекламного бюджета

Или:

если товар требует сертификата и certificate.status != VALID

публикацию запретить

Или:

если order_amount > 500 000 ₽

требовать ручного подтверждения

Таким образом:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Это две разные вещи.

Почему правила лучше не зашивать в интеграции

Плохой сценарий выглядит знакомо:

Через некоторое время компания обнаруживает, что бизнес-логика размазана по десяткам интеграций.

Меняется одно правило.

Например:

нельзя продавать товар ниже маржинальности 12%.

И приходится менять:

WB Connector

Ozon Connector

Yandex Market Connector

Shop Connector

B2B Connector

В orchestration-подходе правило живёт выше:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Каналы остаются каналами.

Бизнес-логика остаётся бизнес-логикой.

А теперь происходит ошибка

До этого момента всё выглядело довольно академично.

Но распределённые системы интересны не тогда, когда всё работает.

Они интересны тогда, когда что-нибудь перестаёт работать.

Представим:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

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 тоже сложнее, чем кажется

Типичный алгоритм начинающего разработчика:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Но далеко не каждую ошибку следует повторять.

Например:

HTTP 500

возможно, временная проблема.

Retry имеет смысл.

А:

SKU_NOT_FOUND

Retry почти наверняка бесполезен.

Или:

CERTIFICATE_EXPIRED

Повтор запроса каждые 30 секунд ничего не исправит.

Поэтому нужна классификация ошибок:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Retry нельзя делать бесконечным

Пусть Ozon API недоступен.

Если у нас 100 000 операций и каждая начинает повторяться каждую секунду:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

мы сами создаём дополнительную нагрузку на уже проблемную систему.

Поэтому нормальный retry обычно выглядит примерно так:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

То есть используется backoff.

Часто с добавлением jitter, чтобы тысячи операций не повторились одновременно.

Но что происходит после последнего retry?

Вот это важный вопрос.

Допустим:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

В плохо спроектированной интеграции ответ:

лог

То есть somewhere:

ERROR failed to update stock

И задача закончилась.

Для Orchestrator этого недостаточно.

После исчерпания retry операция должна перейти в другое состояние:

RETRY_EXHAUSTED

А workflow решить, что делать дальше.

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

То есть человек — тоже часть архитектуры.

Человек в контуре управления

Иногда автоматизацию ошибочно воспринимают как попытку полностью удалить человека из процесса.

На практике хороший Orchestrator должен понимать:

какие решения можно принять автоматически, а какие нельзя.

Представим ситуацию.

ERP говорит:

stock = 12

WMS говорит:

stock = 9

Marketplace сообщает:

available = 15

Какой остаток правильный?

Можно придумать автоматическое правило:

available = min(ERP, WMS)

Получим:

9

Но что, если ERP ещё не получил приход?

Или WMS задержал событие?

Или маркетплейс считает товар, уже находящийся в заказах?

Иногда система не должна угадывать.

Она должна сказать:

CONFLICT_DETECTED

И остановить потенциально опасное действие.

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Это и есть Человек в контуре управления

Очень важная мысль: автоматизация должна уметь не действовать

Большинство систем проектируют вокруг идеи:

получить событие → выполнить действие.

Но для серьёзной автоматизации иногда правильное действие:

ничего не делать автоматически.

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Вместо:

что-нибудь отправим, потом разберёмся

Для commerce это особенно важно, потому что ошибки быстро превращаются в деньги.

Следующий механизм — Reconciliation

Пожалуй, это один из самых недооценённых элементов интеграционной архитектуры.

Допустим, у нас событийная система.

Marketplace изменил остаток:

stock = 10

Отправил событие.

Мы его получили.

Всё отлично.

Но что произойдёт, если одно событие потерялось?

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Локальная система может остаться в неправильном состоянии.

И никто этого не заметит.

Поэтому одного 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 недостаточно

Можно построить красивую архитектуру:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

И считать её надёжной.

Но события тоже:

  • теряются;
  • приходят дважды;
  • приходят не по порядку;
  • задерживаются;
  • содержат старые данные;
  • могут не прийти вообще.

Поэтому зрелая система обычно сочетает:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Первый механизм даёт скорость.

Второй — уверенность.

Простой пример с остатками

Допустим:

Физический остаток: 10

Получили заказ:

-1

Стало:

9

Следующий заказ:

-1

Стало:

8

Но событие второго заказа потерялось.

Orchestrator думает:

9

WMS:

8

Через reconciliation:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

После чего запускается восстановление.

Без reconciliation ошибка могла жить неделями.

И вот теперь всё складывается вместе

Commerce Orchestration — это не один механизм.

Это цепочка:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Именно сочетание этих механизмов превращает интеграцию в оркестрацию.

Разберём один заказ целиком

Теперь попробуем собрать полный сценарий.

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

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Это нормальная архитектура.

Просто у каждого слоя своя ответственность.

ESB

Как доставить сообщение?

Commerce Orchestration

Зачем его отправлять?

Когда?

При каких условиях?

Что делать при ошибке?

Как понять, что процесс закончен?

Аналогично с iPaaS

iPaaS отлично подходит для:

получить данные -> преобразовать -> передать

Но по мере роста бизнеса сценарий постепенно превращается в:

если... -> если... -> проверить... -> повторить... -> подождать... -> если состояние... -> позвать оператора... -> сверить через 30 минут...

И в этот момент integration flow начинает играть роль бизнес-процесса.

Именно здесь и возникает архитектурная граница.

Поэтому я бы определил Commerce Orchestration так

Не:

система интеграции маркетплейсов.

Не:

ещё один ESB.

Не:

коннектор между ERP и маркетплейсами.

А:

Commerce Orchestration — это слой, который хранит состояние торговых процессов, принимает бизнес-решения и координирует выполнение операций между независимыми системами.

Его задача — превратить:

набор API

в:

управляемый бизнес-процесс

Получается следующая архитектура

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Это, пожалуй, и есть главное различие между orchestration и integration.

Integration соединяет системы.

Orchestration управляет тем, что между ними происходит.

Но остаётся ещё один вопрос

До сих пор мы предполагали, что все системы физически существуют.

API может временно не отвечать.

ERP может зависнуть.

Сообщение может потеряться.

Это классические технические сбои.

Но что произойдёт, если проблема серьёзнее?

Например:

Что находится внутри Commerce Orchestration: state, workflows, retries и reconciliation

Или если часть товарного запаса физически становится недоступна?

Вот здесь обычной обработки ошибок уже недостаточно.

Возникает следующая задача:

может ли торговая система продолжить работу после потери одного из своих ключевых элементов?

И это уже переход от Commerce Orchestration к тому, что можно назвать Commerce Resilience.

Но об этом — в следующей статье.

1