Event Contract: pin.calendar.event-owner-changed (CalendarEventOwnerChangedEvent)
Версионируемый контракт интеграционного события «Владелец календарного события переназначен». Consumer-
документы ссылаются сюда, не переописывая payload. Дом полей агрегата —
entity: CalendarEventEntity.
Назначение
Фиксирует переназначение владельца открытого события: у события в Planned/InProgress изменён
OwnerUserId (в пределах того же TenantId) — оно перешло из календаря прежнего агента в календарь нового
исполнителя сделки. Единственный источник переназначения — консюмер deal-событий CRM
(../consumers/crm-deal-events.md) при смене исполнителя
(deal-members-changed ChangeType = ExecutorChanged) или уходе агента из сделки (Removed, в т.ч.
увольнение через CRM-offboarding); санкция — INV-CAL-8 (calendar-event.md).
Публичное: notifications уведомляет нового владельца о появившемся у него обязательстве
(NotificationKind = CalendarEventOwnerChanged), при необходимости — прежнего владельца о снятии — без
синхронного запроса в calendar. Напоминания при переназначении не пересоздаются (моменты DueAt
неизменны — сместился только владелец); их доставку по новому владельцу обеспечивает calendar.
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.calendar.event-owner-changed |
| Topic | pin.calendar.event-owner-changed.v1 |
| Partitions / key | 3 (ADR-0056) / key = EventId (порядок событий одного календарного события) |
| ContractVersion | 1 |
| Producer | calendar: консюмер deal-событий CRM (../consumers/crm-deal-events.md) — переназначение владельца открытого события при смене исполнителя / уходе агента (INV-CAL-8) |
| Статус | draft |
Событие публикуется атомарно с изменением агрегата (сменой OwnerUserId в том же согласованном
изменении): пока переназначение не зафиксировано, событие не отправляется; после фиксации — доставляется
гарантированно (at-least-once, transactional outbox), ключ = EventId.
События Origin = Appointment не переназначаются (управляются жизненным циклом записи, INV-CAL-3) и
этим контрактом не покрываются.
Payload CalendarEventOwnerChangedEvent
Дом полей — entity: CalendarEventEntity; здесь перечислено только то, что
реально в событии. OwnerUserId — новое значение после переназначения.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
EventId | Guid | Да | CalendarEventEntity.Id | Immutable | Ключ агрегата (= Kafka key). |
TenantId | Guid | Да | CalendarEventEntity.TenantId | Immutable | Компания владельца календаря (ADR-0052); при переназначении не меняется (новый исполнитель — тот же AgencyCompanyId). |
OwnerUserId | Guid | Да | CalendarEventEntity.OwnerUserId | Immutable (значение на момент публикации) | Новый владелец — исполнитель сделки, получатель обязательства. |
PreviousOwnerUserId | Guid | Да | прежний OwnerUserId события | Additive only | Прежний владелец — у него событие снято из календаря; получатель уведомления о снятии. |
Kind | string (enum EventKind) | Да | CalendarEventEntity.Kind | Additive only (новые значения) | Вид события (BookingDeadline / Meeting / Showing / Consultation / Task). |
Origin | string (enum EventOrigin) | Да | CalendarEventEntity.Origin | Additive only (новые значения) | CrmBooking (дедлайн брони следует за исполнителем) / Manual (встреча ушедшего агента); Appointment не встречается. |
StartAt | DateTimeOffset | Да | CalendarEventEntity.StartAt | Immutable (значение на момент публикации) | Начало события, UTC — для текста уведомления. |
EndAt | DateTimeOffset? | Нет | CalendarEventEntity.EndAt | Immutable (значение на момент публикации) | Конец, UTC; null — точечное событие (дедлайн/задача). |
TimeZone | string | Да | CalendarEventEntity.TimeZone | Additive only | IANA-таймзона — для отображения локального времени в уведомлении. |
DealId | Guid? | Нет | CalendarEventEntity.DealId | Additive only | Сделка CRM — причина переназначения (логическая ссылка). |
Когда публикуется
| Условие | Переход состояния | Публикация |
|---|---|---|
Смена исполнителя сделки (deal-members-changed ExecutorChanged) — открытый BookingDeadline следует за новым исполнителем | статус не меняется; OwnerUserId изменён | атомарно с изменением агрегата |
Уход агента из сделки (Removed / увольнение) — открытые встречи/задачи ушедшего агента переходят к исполнителю | статус не меняется; OwnerUserId изменён | та же |
Каждое фактическое переназначение публикуется отдельным сообщением. Совпадение источника и цели
(OwnerUserId уже = новый исполнитель) события не порождает.
Потребители (consumers)
| Consumer-контекст | Side effects | Идемпотентность | Задержка (SLA) |
|---|---|---|---|
notifications | Уведомление о переназначении (NotificationKind = CalendarEventOwnerChanged): новому владельцу — «на вас переведено обязательство сделки»; прежнему (PreviousOwnerUserId) — «обязательство снято». Консюмер — ../../notifications/consumers/calendar-events.md | MessageId | < 5 мин |
Версионирование и совместимость
ContractVersion = 1; при несовпадении версии контракта потребитель логирует предупреждение и продолжает обработку. Топик кодирует версию по конвенции суффиксом.v1.- Additive-only для minor: новые поля добавляются как optional с безопасным default — без bump.
- Breaking —
ContractVersion = 2+ новый топикpin.calendar.event-owner-changed.v2+ dual-publish + deadline миграции consumers + sunset.v1. - Unknown enum (
Kind/Origin) — fallback: сохранить строку как есть, warning, уведомление с нейтральным текстом (fail safe).
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
EventId, TenantId, OwnerUserId, PreviousOwnerUserId, Kind, Origin, StartAt, EndAt, TimeZone, DealId | Internal | include (только идентификаторы, время и enum) |
- PII (ФИО/телефоны) в событии отсутствует по дизайну — display-данные владельцев консюмеры берут из
своей реплики
pin.identity.users.v1;Title/Description/Locationв payload не входят. - Доступ к топику — m2m ACL Kafka (
notifications).
Доставка, retry/DLQ и восстановление
- Гарантия. At-least-once после атомарной публикации из outbox; ключ =
EventId— переназначения одного события приходят по порядку. Дубликаты возможны — консюмер идемпотентен поMessageId. - Retry. Транзиентный сбой консюмера → повтор с backoff; порядок по ключу сохраняется.
- DLQ. Неразбираемое/несовместимое сообщение после конечного числа попыток — в DLQ с алертом; остальные события не блокируются.
- Кросс-топик порядок. С event-status-changed порядок между топиками не гарантирован: если событие уже отменено/завершено на момент обработки переназначения, уведомление о новом владельце не отправляется (fail-safe по бизнес-состоянию, warning); финальную картину даёт уведомление об отмене/завершении.
- Промежуточное состояние. Новый владелец видит событие в своём календаре сразу; уведомление — после обработки события (обычно < 5 мин).
Acceptance-критерии (контракт потребления)
- Переназначение при смене исполнителя. Дано: открытый
BookingDeadlineсделки переназначен сAнаB. Когдаnotificationsобрабатывает событие. ТогдаBполучает уведомлениеCalendarEventOwnerChangedо новом дедлайне;A— уведомление о снятии. - Переназначение встречи ушедшего агента. Дано: открытая
Meetingсо-агентаCпереназначена на исполнителяE. Когдаnotificationsобрабатывает событие. ТогдаEуведомлён о встрече. - Идемпотентность. Дано: сообщение доставлено дважды. Тогда дубль уведомления не создаётся.
- Терминальное событие. Дано: событие уже отменено на момент обработки переназначения. Тогда уведомление о новом владельце не отправляется (fail-safe), warning.
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (1): Entity / Aggregate: NotificationEntity
- Use Cases (1): UC-CAL-011. Переназначение открытых дедлайнов и встреч при смене исполнителя
- Консюмеры (2): Consumer: Calendar CRM Deal Sync (calendar-crm-deal-events), Consumer: Calendar Events → Notifications (notifications-calendar-events)
- registries (1): Реестр событий PIN
Связанные документы
- Сущность/дом полей: calendar-event.md (INV-CAL-8 — санкционированное
переназначение владельца;
ix_calendar_events_deal_id). - Producer: ../consumers/crm-deal-events.md (реплика агентов сделки, алгоритм переназначения); источник смены состава — deal-members-changed.md, каскад увольнения — agent-offboarding.md.
- Соседние контракты семейства: event-created.md, event-rescheduled.md, event-status-changed.md, reminder-due.md.
- ADR: 0064 calendar-контекст, 0052 tenancy, 0072 каскад увольнения агента.