АрхитектураМикросервисы и интеграцииРазработчик интеграционных решений

Сервис доставки получает событие заказа от сервиса продаж, но одинаковое поле status имеет разные бизнес см...

Сервис доставки получает событие заказа от сервиса продаж, но одинаковое поле status имеет разные бизнес-смыслы:

{"orderId":"42","status":"READY"}

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

Проходите собеседования с ИИ помощником Hintsage

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

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

Прямое присваивание внешнего status внутреннему полю опасно: одинаковое слово может обозначать разные состояния, а изменение внешнего контракта начнёт менять внутреннюю бизнес-логику.

Исторический контекст

Антикоррупционный слой появился как практический паттерн интеграции независимых моделей в Domain-Driven Design. В распределённых системах разные команды моделируют один и тот же предмет с собственными терминами, инвариантами и жизненными циклами.

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

Постановка проблемы

Сервис продаж может считать READY состоянием «заказ оплачен и готов к передаче в доставку». Сервис доставки может использовать READY только для означения «посылка собрана и ожидает курьера».

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

Подробное решение

Антикоррупционный слой принимает внешний контракт, валидирует его и преобразует в внутреннюю команду или внутреннее событие. Внутри сервиса доставки используются его собственные типы и термины, а не названия из сервиса продаж.

{ "externalStatus": "READY", "internalAction": "CREATE_SHIPMENT" }

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

Граница должна находиться на входе в сервис: после преобразования доменные объекты не должны зависеть от внешнего JSON, названий полей или идентификаторов. Для исходящих сообщений применяется обратное преобразование — внутреннее состояние публикуется в отдельном внешнем контракте.

Компромисс — дополнительный код, тесты и необходимость поддерживать таблицу соответствий. Это оправдано, когда модели действительно принадлежат разным bounded context; если сервисы имеют одного владельца и одну модель, чрезмерный перевод может усложнить систему без архитектурной пользы.

Ситуация из практики

Сервис продаж публиковал READY, CANCELLED и RETURNED. Сервис доставки напрямую сохранял эти значения в собственной таблице, хотя его жизненный цикл включал состояния AWAITING_PICKUP, IN_TRANSIT и DELIVERED.

Рассматривались два варианта. Первый — использовать общую библиотеку перечислений: это уменьшало дублирование, но связывало релизы и навязывало одну модель двум доменам. Второй — принимать внешний статус напрямую: решение было простым, но скрывало семантическое несовпадение и допускало некорректные переходы.

Выбрали антикоррупционный слой с явной таблицей преобразований и контрактными тестами на входные события. Для READY создавалась команда CREATE_SHIPMENT, а неподдерживаемые комбинации попадали в очередь ошибок. В результате изменение статусов в продажах не меняло внутренний автомат доставки, а спорные соответствия обнаруживались до обработки бизнес-операции.

Что кандидаты часто упускают

  1. Чем антикоррупционный слой отличается от обычного маппинга DTO?

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

  1. Где должна находиться логика преобразования — у отправителя или получателя?

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

  1. Что делать, если одно внешнее состояние соответствует нескольким внутренним?

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