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