Продюсер события не ждёт потребителей: связность уходит в контракт топика и в эволюцию схемы. Имя события, владелец полей и совместимость — часть дизайна, а не «потом добавим ключ в JSON».
Проектирование сервисов architecture собеседование
Команда — «сделай»: PlaceOrder, синхронный запрос с ожидаемым ответом. Событие — «уже случилось»: OrderPlaced.
Продюсер пишет в брокер и живёт дальше. Он не держит список подписчиков в коде. Консьюмеры масштабируются сами.
Это плюс и минус. Проще добавить нового читателя без релиза отправителя. Легче выкатить ломающую схему и узнать об этом по ночным падениям чужого сервиса.
Пример. At-least-once почти всегда: одно и то же событие могут доставить дважды. Консьюмер обязан быть идемпотентным. Иначе два письма и две проводки.
Связность через событие
Синхронный REST: в коде видно, кого зовёшь. Событие UserUpdated с полным слепком пользователя тянет всех читателей к вашей модели. Сменили вложенный адрес — переломали чужой парсер.
Лучше факты домена с минимумом полей: EmailChanged, AddressAdded. «Бог-событие» на каждое движение колонки — скрытая связность, как общая БД, только с лагом.
Владелец схемы — команда продюсера. Читатели не имеют права молча требовать новое поле «к пятнице» без версии и политики совместимости.
Эволюция схемы
- Добавлять опциональные поля обычно безопасно: protobuf не переиспользует номера, JSON-читатель игнорирует неизвестные ключи.
- Переименовать, удалить или сменить смысл поля ломает старых консьюмеров. Dual-read, версия в заголовке или новый тип события.
- Порядок в партиции: ключ агрегата, чтобы
EmailChangedне обогналUserCreatedна одном пользователе.
{
"type": "EmailChanged",
"version": 1,
"key": "user-42",
"user_id": "42",
"new_email": "a@b.c",
"changed_at": "2026-08-27T11:00:00Z"
}
// Не так: UserUpdated { user: { ...весь агрегат... } }
// любой читатель садится на вашу модель
// и ломается при лишнем поле.
Факт домена с версией схемы. Жирный UserUpdated тянул бы всех к вашей модели.
Доставка и граница с БД
Продюсер после commit в своей БД должен гарантировать публикацию: transactional outbox или тот же диск, что и данные.
Консьюмер пишет inbox по event_id, затем применяет эффект. Повтор доставки не создаёт вторую проводку. Это та же дисциплина, что идемпотентный HTTP POST, только курсор брокера вместо клиента.
Типичная ошибка
Считать, что «событие само идемпотентно». Идемпотентен консьюмер, не топик. Вторая ошибка — ломающая смена смысла поля без версии. Третья — ключ партиции round-robin: факты одного агрегата переупорядочиваются.
На собеседовании
Спросите имя события, владельца схемы и ключ партиции. Coupling через топик не исчезает — он становится контрактом во времени. Мелкие факты домена плюс outbox/inbox и идемпотентный consumer — рабочий минимум.