АрхитектураМикросервисы и интеграцииАрхитектор распределённых систем

Сервис хочет использовать внутренние доменные события как публичный интеграционный контракт. Какой архитект...

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

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

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

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

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

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

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

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

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

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

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

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

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

Между внутренним событием и внешним контрактом вводят явное преобразование. Сначала сервис фиксирует внутренний факт, например изменение состояния заказа, затем преобразует его в интеграционное событие с определённой аудиторией, семантикой и схемой.

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

Такое разделение позволяет внутреннему событию меняться независимо. Например, сервис может заменить несколько внутренних переходов одним внешним фактом, если сохраняется обещанная потребителям бизнес-семантика.

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

Цена разделения — дополнительная модель, код преобразования и необходимость управлять двумя схемами. Это оправданный компромисс, если событие используется независимыми командами или должно жить дольше текущей реализации сервиса. Для чисто внутренней коммуникации отдельный внешний контракт может быть избыточен.

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

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

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

Выбрали второй вариант. Сервис заказов стал публиковать стабильный внешний контракт, а сервис доставки реагировал только на обещанный бизнес-факт. После изменения внутреннего автомата состояния интеграция не потребовала изменений, поскольку внешний смысл события сохранился.

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

  1. Обязательно ли каждое доменное событие преобразовывать в отдельное интеграционное событие?

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

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

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

  1. Может ли интеграционное событие содержать больше данных, чем внутреннее доменное событие?

Да. Внешний контракт может быть собран из нескольких внутренних источников или обогащён данными, необходимыми потребителям. При этом сервис должен гарантировать корректную семантику и согласованность опубликованного факта; нельзя выдавать за единое событие набор данных, собранных из несовместимых состояний.