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

События сервиса messenger: реестр топиков

Индекс интеграционных событий pin.messenger.* (по одному контракт-доку на топик) + дом решений об интеграциях сервиса: что публикуется наружу и почему. Минимальность — ADR-0056: топик существует только при живом потребителе, которому синхронный API не подходит. Realtime-доставка (SignalR persist-then-push, ADR-0055) — не Kafka: in-app push идёт из hub напрямую, наружу через Kafka уходят только события с внешними потребителями (crm, notifications).

Metadata

ПолеЗначение
Owning teamPIN Platform (Comms squad)
Статус документаdraft
Область действияконтекст messenger, папка events/ (produced-контракты)
Assumptionscontract-доки перечислены ниже; дом полей payload — доменные агрегаты domain/*; consumed-события — источники crm/identity
ЗависимостиKafka (outbox producer), crm / notifications (downstream-консюмеры), SignalR-hub (realtime, вне Kafka)
Change contextgreenfield (ADR-0055)

Общие правила

Все produced-топики публикуются через транзакционный outbox атомарно (в одном согласованном изменении) с мутацией агрегата (persist-then-push). У всех ContractVersion = 1; версия кодируется суффиксом .v1 в имени топика по конвенции.

Строгая типизация payload — только id (Guid), enum-строки, bounded-скаляры; тела сообщений, ФИО/телефоны, IP/UA, secrets и платёжные данные в события не попадают (дом правил — доменные агрегаты).

Публикуемые события (produced)

TypeСобытиеTopicConsumersНазначениеКонтракт
ProducedDealChatStatusChangedEventpin.messenger.deal-chat-status-changed.v1crmГотовность чата сделки (Provisioning/Ready/Failed) → проекция Deal.ChatStatusdeal-chat-status-changed.md
ProducedMessagePostedEventpin.messenger.message-posted.v1notificationsФакт отправки сообщения → offline push/email адресатам (bounded-preview, без тела/PII)message-posted.md
ProducedContactRequestCreatedEventpin.messenger.contact-request-created.v1notificationsСоздана заявка в контакты → уведомить адресатаcontact-request-created.md
ProducedContactRequestAcceptedEventpin.messenger.contact-request-accepted.v1notificationsЗаявка подтверждена → уведомить инициатораcontact-request-accepted.md
ProducedConferenceEndedEventpin.messenger.conference-ended.v1crm, notifications, dev-statsГрупповой звонок сделки завершён → карточка в таймлайн сделки + аналитикаconference-ended.md
ProducedCallRecordingAvailableEventpin.messenger.call-recording-available.v1crm, notificationsЗапись звонка готова (file storage FileId) → прикрепить к сделке + уведомить участниковcall-recording-available.md

Ключи партиционирования

TopicPartitionsKafka keyПорядок
deal-chat-status-changed.v13DealIdper-deal смены статуса
message-posted.v16ChatIdper-chat порядок сообщений
contact-request-created.v13ContactIdper-связь (createdaccepted)
contact-request-accepted.v13ContactIdper-связь (createdaccepted)
conference-ended.v13DealIdper-deal конференции
call-recording-available.v13SessionIdper-session сегменты записи

Гарантии доставки, порядок и обработка отказов

Единые правила для всех produced-топиков сервиса (детали и коды — в контракт-доках). Каждый контракт описывает только дельту к этим правилам.

  • Доставка — at-least-once. Публикация атомарна с мутацией агрегата (outbox): пока изменение не зафиксировано, событие не отправляется; после фиксации доставляется хотя бы раз. Потребитель обязан быть идемпотентным — повтор доставки не должен давать двойного эффекта. Ключ идемпотентности каждого топика — бизнес-id из payload (MessageId / RecordingId / ConferenceId / ContactId / DealId), дедуп — first-seen.
  • Порядок — только per-key. Гарантируется внутри одного ключа партиционирования (таблица выше): сообщения одного чата (ChatId), смены статуса одной сделки (DealId), жизненный цикл одной связи (ContactId), сегменты одной сессии (SessionId). Межтопикового и межключевого порядка нет — потребитель, которому нужна причинность двух топиков (напр. conference-ended перед call-recording-available), не полагается на очередь, а сверяет по бизнес-полям (RecordingId/WasRecorded) и терпит запаздывание.
  • Retry/DLQ. Транзиентный сбой потребителя (БД/сеть) — ретрай с backoff по offset; после исчерпания попыток сообщение уходит в DLQ топика + алерт (runbook), offset продвигается, поток не блокируется. Исключение — deal-chat-status-changed: неизвестный Status критичен для воронки сделки → CRM_CHAT_STATUS_UNKNOWN → DLQ + алерт сразу (не тихий fallback). Для остальных unknown enum — fallback: строка как есть + warning (уведомление/эффект деградирует, не падает).
  • Частичный отказ fan-out. Если у события несколько потребителей (conference-endedcrm + notifications + dev-stats; call-recording-availablecrm + notifications), они независимы: падение одного не откатывает других; редоставка повторяет всех, идемпотентность гасит дубли.

Наблюдаемость пайплайна событий

  • Метрики: размер и возраст outbox-backlog (алерт > 1000 / lag > 5 мин, README); лаг консюмеров crm/identity; счётчик сообщений в DLQ по топику; publish-rate по каждому produced-топику.
  • Логи публикации/потребления несут только id/enum + traceparent (корреляция сквозная); тела сообщений, ФИО/телефон, IP/UA в события и логи не попадают (INV-MSG-6).

Потребляемые события (consumed)

Дом контрактов — сервисы-источники; здесь только карта (consumer-доки — ../consumers/).

TypeTopicИсточникConsumer-эффектConsumer-док
Consumedpin.crm.deal-created.v1crmПровизия Deal-чата + участники → deal-chat-status-changeddeal-created.md
Consumedpin.crm.deal-stage-changed.v1crmИнфо-карточка этапа; терминальный ToStatus → закрытие чатаdeal-stage-changed.md
Consumedpin.crm.deal-members-changed.v1crmSet-реконсиляция состава чата (INV-CHAT-6)deal-members-changed.md
Consumedpin.crm.document-requested.v1crmКарточка целевого действия «Загрузите документы» (Pending)document-requested.md
Consumedpin.crm.booking-paid.v1crmИнфо-карточка «Договор бронирования оплачен»booking-paid.md
Consumedpin.crm.booking-expired.v1crmКарточка «Бронь истекла — подтвердите/продлите»booking-expired.md
Consumedpin.identity.users.v1identityРеплика ФИО/телефона (MessengerUserEntity) + реиндексация search-документа контактов (Elasticsearch)identity-user-upserted.md

Сознательно НЕ публикуется (ADR-0056)

  • Правка/удаление сообщения, Decline/Cancel/Remove контакта — только in-app push в SignalR-hub (MessageEdited/MessageDeleted/ContactRequestDeclined/ContactRemoved); внешних потребителей, которым нужен Kafka, нет — при reconnect клиент перезапрашивает REST (realtime — не source of truth).
  • EventCard-карточки — материализуются консюмерами crm внутри messenger идемпотентно (INV-MSG-8), наружу не реэмитятся (у notifications свои триггеры уведомлений сделки).
  • Тело сообщения, ФИО/телефон, IP/UA — PII/Sensitive, не покидают границу messenger (INV-MSG-6): потребители резолвят display из собственных реплик identity.
  • Сигналинг звонков 1:1 (start/answer/end) — приглашение и lifecycle идут через hub (ADR-0055), медиа — клиент ↔ SFU (ADR-0062); в Kafka не уходят (person-to-person, вне сделки, внешнего потребителя нет), call-лог хранится локально (call-session.md). В отличие от них, конференции сделки (conference-ended) и готовность записи (call-recording-available) публикуются — они пересекают границу CRM (сделка/уведомления) и синхронным API не покрываются.
  • Транскрипт/конспект встречи (pin.messenger.call-transcript-available) — запланировано, не публикуется, пока нет живого потребителя; форма зеркалит call-recording-available (см. ../calls/transcription-roadmap.md).

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