Сервис публикует событие на каждое изменение внутренней таблицы. Какой архитектурный риск создаёт такой контракт?
Такой контракт связывает потребителей с внутренней моделью хранения сервиса. Изменение таблицы, не имеющее самостоятельного бизнес-смысла, становится внешним обязательством и затрудняет эволюцию схемы. Публиковать следует стабильные интеграционные события, описывающие значимые бизнес-факты, а не механические изменения persistence-модели.
В ранних интеграциях сервисы часто передавали наружу записи таблиц или сообщения об их изменении. Это казалось простым способом синхронизации: изменил строку — отправил такое же изменение потребителям.
Проблема проявилась при разделении ответственности между сервисами. Внутренняя схема хранения стала меняться независимо, но любое переименование поля, нормализация таблицы или разбиение сущности начало требовать согласованных изменений всех потребителей.
Изменение строки не обязательно является бизнес-событием. Например, обновление служебного признака, перестроение агрегата или перенос данных между таблицами может не менять бизнес-состояние, но при публикации изменений заставляет потребителей реагировать на технические детали.
Такой контракт создаёт несколько рисков:
Главное последствие — сервис уже не может свободно менять внутреннюю реализацию. Формально база данных остаётся локальной, но фактически её модель становится распределённым API.
Сервис должен отделять события домена или интеграционные события от изменений хранения. Сначала он определяет бизнес-факт, который важен другим участникам, например «заказ подтверждён» или «лимит клиента изменён». Затем формирует отдельный контракт события с понятными бизнес-свойствами и публикует его через контролируемый механизм доставки.
Интеграционное событие должно быть самодостаточным в пределах своего смысла, иметь устойчивые идентификаторы, явную семантику и правила эволюции. Потребителю не следует сообщать, в какой таблице хранилось значение или какой внутренний шаг привёл к событию.
Это не означает, что каждое событие обязано содержать полный снимок сущности. Полный снимок упрощает построение новых проекций, но увеличивает размер сообщений и риск раскрытия лишних данных. Малое событие уменьшает связанность, однако может вынудить потребителя дополнительно обращаться к источнику или самостоятельно поддерживать больше состояния.
Нельзя автоматически считать любое изменение бизнес-данных отдельным интеграционным событием. Например, внутренние переходы статуса могут быть важны для аудита, но не должны становиться публичными, если потребителям нужен только итоговый бизнес-факт.
На практике полезно разделять:
Разделение добавляет преобразование и дополнительную поддержку контрактов. Зато оно сохраняет автономию сервиса: внутренняя схема может меняться без публикации каждого технического шага. Для надёжной доставки это преобразование и запись факта публикации обычно связывают с бизнес-изменением через механизм, устраняющий расхождение между транзакцией данных и отправкой сообщения, но сам выбор события всё равно остаётся архитектурным решением.
Сервис заказов публиковал сообщения о каждой операции над таблицей заказа. Потребитель строил поисковый индекс, а после перехода на новую схему хранения выяснилось, что один бизнес-заказ теперь обновляется несколькими техническими записями. Индекс начал получать промежуточные состояния и зависеть от порядка внутренних операций.
Рассматривались два варианта. Первый — сохранить поток изменений таблицы и заставить потребителя понимать все технические операции. Его плюс — почти отсутствие логики преобразования на стороне источника, но цена — сильная связанность, сложное восстановление состояния и хрупкость при миграциях.
Второй — публиковать отдельные события вроде «заказ создан», «позиция добавлена» и «заказ подтверждён», не раскрывая структуру хранения. Он требовал определить бизнес-семантику и поддерживать отдельный контракт, зато потребитель перестал зависеть от промежуточных записей.
Выбрали второй вариант. Внутреннюю последовательность изменений оставили локальной, а наружу отправляли только согласованные бизнес-факты. В результате миграция таблиц не требовала изменений поискового индекса, а сообщения стали пригодны для повторной обработки и построения новых потребителей.
Нет. Важно изменить не название, а уровень абстракции и ответственность за данные. Событие должно отвечать на вопрос, какой бизнес-факт произошёл и что потребитель вправе из него заключить. Если под новым названием всё ещё передаются поля таблицы, контракт остаётся связанным с внутренней моделью.
Не обязательно. Публикация зависит от потребностей интеграции и выбранной семантики. Иногда важен каждый значимый переход, иногда — только факт достижения итогового состояния; это должно быть явно определено контрактом. Скрывать существенные переходы нельзя, но и технический шум не следует выдавать за бизнес-события.
Нужно определить отдельный стабильный контракт изменений, а не отдавать ему доступ к таблице. Такой контракт может содержать версию агрегата, идентификатор сущности, тип бизнес-изменения и необходимые данные, но не обязан повторять структуру хранения. Если требуется полное восстановление, дополнительно проектируют снимки или поток событий с ясной семантикой, сохраняя право источника менять свои таблицы.