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