Перейти к основному содержимому

Event Contract: pin.crm.booking-cancelled (BookingCancelledEvent)

Версионируемый контракт интеграционного события «бронирование отменено». Consumer-документы ссылаются сюда, не переописывая payload. Дом полей агрегата — entity: BookingEntity.

Назначение

Фиксирует ручную отмену процесса брони до его истечения: инвестор передумал, агент/менеджер меняет объект недвижимости, сделка отклонена. Переход агрегата BookingEntity из активного статуса (Reserved / AwaitingPayment / Booked) в терминальный Cancelled.

Событие публичное: даёт chessboard сигнал освободить объект недвижимости (Резерв/Бронь → Свободен) без синхронного запроса в crm — сменой статуса объекта в шахматке владеет и выполняет chessboard как консюмер booking-событий (правила границ, service-boundaries; решение 5 контекста). Отличается от booking-expired: то — истечение таймера проверкой, это — явная отмена актором.

Metadata

ПолеЗначение
Event typepin.crm.booking-cancelled
Topicpin.crm.booking-cancelled.v1
Partitions / key3 (низкий объём booking-событий, ADR-0056) / key = BookingId (= AggregateId)
ContractVersion1
Producercrm: отмена брони (актор инвестор/агент/менеджер застройщика/Менеджер ПИН) и каскад отмены сделки (deal-cancelled)
Статусdraft

Событие публикуется атомарно с переходом агрегата: пока отмена не зафиксирована, событие не отправляется; после фиксации — доставляется гарантированно (at-least-once), ключ = BookingId.

Payload BookingCancelledEvent

Дом полей — entity: BookingEntity (раздел «Доменные и интеграционные события»); здесь перечислено только то, что реально в событии. Snapshot-полей условий программы событие не несёт (отмена не зависит от условий).

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
BookingIdGuidДаBookingEntity.IdImmutableКлюч агрегата (= Kafka key, AggregateId).
DealIdGuidДаBookingEntity.DealIdImmutableСделка-владелец процесса (deal.md).
TenantIdGuidДаBookingEntity.TenantIdImmutableTenant boundary — застройщик объекта (DeveloperCompanyId).
PropertyObjectIdGuidДаBookingEntity.PropertyObjectIdImmutableОбъект в chessboard, который освобождается (→ Свободен).
CancelledByUserIdGuidДаBookingEntity.UpdatedByUserId (актор отмены)ImmutableКто отменил (инвестор/агент/менеджер застройщика/Менеджер ПИН). Отмена всегда инициируется пользователем — не проверкой по таймеру.
CancelledAtDateTimeOffsetДаBookingEntity.CancelledAtImmutableМомент отмены (иммутабельный факт фазы).
CancelReasonstringДаBookingEntity.CancelReasonAdditive onlyПричина отмены (обязательна при Status = Cancelled, ≤ 1000). Свободный текст — см. правило безопасности.
Что не входит в payload

Событие НЕ несёт RequisiteSetId, PaymentAmount, документов брони, ФИО/телефона клиента (эти данные живут в lead.md / identity / profile, консюмерам отмены не нужны).

Когда публикуется

УсловиеПереход состоянияПубликация
Явная отмена активной брони актором (INV-BKG-8: терминальные статусы иммутабельны)Reserved / AwaitingPayment / BookedCancelledатомарно со сменой состояния агрегата
Каскад при отмене сделки: отмена сделки отменяет активную бронь тем же изменениемтот же переход → Cancelledта же
Отличие от booking-expired

Истечение таймера (Reserved/Booked → ReserveExpired/BookingExpired проверкой) публикуется отдельным событием booking-expired, а не этим — оба, попадая на консюмер chessboard, освобождают объект недвижимости.

Потребители (consumers)

Сверено с booking.md («Доменные и интеграционные события») и README («Events И Consumers»).

Consumer-контекстSide effectsИдемпотентностьЗадержка (SLA)
chessboardПроекция статуса объекта недвижимости PropertyObjectId: Резерв/БроньСвободенMessageId + per-property-object упорядочение (key BookingId, guard по статусу объекта недвижимости)< 1 мин (актуальность шахматки)
notificationsУведомление участникам сделки об отмене брониMessageId< 5 мин
Идемпотентность и гонки

chessboard идемпотентен и fail-safe: если объект недвижимости уже Свободен/Продан (гонка с booking-expired или завершением сделки) — переход не применяется (no-op), warning в лог.

Версионирование и совместимость

  • ContractVersion = 1; при несовпадении версии контракта потребитель логирует предупреждение и продолжает обработку. Топик кодирует версию по конвенции — суффикс .v1.
  • Additive-only для minor: новые поля добавляются как optional с безопасным default — без bump.
  • Breaking (удаление/переименование/смена типа/смысла) — новый ContractVersion = 2 + новый топик pin.crm.booking-cancelled.v2 + dual-publish на период миграции + deadline миграции consumers + sunset .v1.
  • Порядок per-booking гарантирован key-ом BookingId: booking-created.v1booking-cancelled.v1 одного агрегата упорядочены. Кросс-топик порядок (cancelled vs expired одного объекта недвижимости) — не гарантирован; консюмер chessboard разрешает конфликт по идемпотентности (целевой статус Свободен для обоих).

Безопасность

ПолеКласс данныхПравило
BookingId, DealId, TenantId, PropertyObjectId, CancelledByUserIdInternalinclude
CancelledAtInternalinclude
CancelReasonInternal (свободный текст)include; в логах producer/consumer — не выводить полностью (bounded ≤ 1000, потенциально содержит имена/детали → маскирование в логах)
PII и логирование CancelReason

Событие не содержит ФИО, телефонов, платёжных реквизитов, документов, secrets, tokens — PII клиента (ФИО/телефон) в этом контракте отсутствует, поэтому маскирование payload не требуется; телефоны/ФИО живут в identity/lead.md и в booking-событиях не публикуются. Единственное правило — не логировать CancelReason целиком (free-text). Доступ к топику — m2m ACL Kafka (chessboard, notifications).

Доставка, retry/DLQ и восстановление

  • Гарантия. At-least-once после атомарной публикации; ключ = BookingId — per-booking порядок сохранён. Дубликаты возможны — консюмеры идемпотентны.
  • Идемпотентность. chessboardMessageId + guard по статусу объекта недвижимости (освобождение уже свободного/проданного = no-op); notifications — по MessageId.
  • Retry. Транзиентный сбой консюмера → повтор с backoff без продвижения оффсета этой брони; «объект недвижимости уже Свободен» — no-op, не ошибка.
  • DLQ. Неразбираемое/несовместимое сообщение после конечного числа попыток → DLQ + алерт; другие брони не блокируются.
  • Кросс-топик порядок с booking-expired. Отмена и истечение одного объекта недвижимости целятся в один статус «Свободен»; порядок между топиками не гарантирован, но результат один — chessboard идемпотентен и fail-safe (повторное освобождение = no-op). Если раньше уже обработано завершение сделки (объект недвижимости «Продан») — отмена брони не возвращает объект недвижимости в «Свободен» (guard по статусу).
  • Каскад отмены сделки. Когда бронь отменяется каскадом отмены сделки, booking-cancelled и deal-cancelled — отдельные события; chessboard освобождает объект недвижимости по любому из них (идемпотентно), не завися от порядка их доставки.
  • Промежуточное состояние. В карточке сделки отмена видна сразу (данные crm); освобождение объекта недвижимости в шахматке и уведомление участникам — с задержкой (< 1–5 мин).

Acceptance-критерии (контракт потребления)

  • chessboard — освобождение. Дано: опубликован BookingCancelledEvent, объект недвижимости в «Резерв»/ «Бронь». Когда chessboard обрабатывает. Тогда объект недвижимости → «Свободен».
  • Идемпотентность / no-op. Дано: объект недвижимости уже «Свободен»/«Продан» (гонка с -expired/ завершением сделки). Когда приходит отмена. Тогда переход не применяется (no-op, warning в лог).
  • notifications. Дано: событие опубликовано. Когда notifications обрабатывает. Тогда участники сделки получают уведомление об отмене брони.
  • Каскад отмены сделки. Дано: сделка отменена, активная бронь была. Когда доставлены booking-cancelled и deal-cancelled в любом порядке. Тогда объект недвижимости освобождён ровно один раз (идемпотентно).
  • Несовместимый payload. Дано: сообщение без обязательного поля. Когда повтор не помог. Тогда DLQ + алерт.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы

  • Сущность (дом полей): booking.md; родительский процесс: deal.md.
  • Соседние контракты booking-топиков: booking-created.md, booking-confirmed.md, booking-paid.md, booking-expired.md; связанное каскадом: deal-cancelled.md.
  • Источник (операция): команда отмены брони crm (INV-BKG-8).
  • Консюмер статуса объекта недвижимости — docs/06-services/chessboard/ (будущий путь по конвенции).