События сервиса messenger: реестр топиков
Индекс интеграционных событий pin.messenger.* (по одному контракт-доку на топик) + дом решений об
интеграциях сервиса: что публикуется наружу и почему. Минимальность — ADR-0056: топик существует только
при живом потребителе, которому синхронный API не подходит. Realtime-доставка (SignalR persist-then-push,
ADR-0055) — не Kafka: in-app push идёт из hub напрямую, наружу через Kafka уходят только события с
внешними потребителями (crm, notifications).
Metadata
| Поле | Значение |
|---|---|
| Owning team | PIN Platform (Comms squad) |
| Статус документа | draft |
| Область действия | контекст messenger, папка events/ (produced-контракты) |
| Assumptions | contract-доки перечислены ниже; дом полей payload — доменные агрегаты domain/*; consumed-события — источники crm/identity |
| Зависимости | Kafka (outbox producer), crm / notifications (downstream-консюмеры), SignalR-hub (realtime, вне Kafka) |
| Change context | greenfield (ADR-0055) |
Общие правила
Все produced-топики публикуются через транзакционный outbox атомарно (в одном согласованном изменении)
с мутацией агрегата (persist-then-push). У всех ContractVersion = 1; версия кодируется суффиксом .v1 в
имени топика по конвенции.
Строгая типизация payload — только id (Guid), enum-строки, bounded-скаляры; тела сообщений, ФИО/телефоны, IP/UA, secrets и платёжные данные в события не попадают (дом правил — доменные агрегаты).
Публикуемые события (produced)
| Type | Событие | Topic | Consumers | Назначение | Контракт |
|---|---|---|---|---|---|
| Produced | DealChatStatusChangedEvent | pin.messenger.deal-chat-status-changed.v1 | crm | Готовность чата сделки (Provisioning/Ready/Failed) → проекция Deal.ChatStatus | deal-chat-status-changed.md |
| Produced | MessagePostedEvent | pin.messenger.message-posted.v1 | notifications | Факт отправки сообщения → offline push/email адресатам (bounded-preview, без тела/PII) | message-posted.md |
| Produced | ContactRequestCreatedEvent | pin.messenger.contact-request-created.v1 | notifications | Создана заявка в контакты → уведомить адресата | contact-request-created.md |
| Produced | ContactRequestAcceptedEvent | pin.messenger.contact-request-accepted.v1 | notifications | Заявка подтверждена → уведомить инициатора | contact-request-accepted.md |
| Produced | ConferenceEndedEvent | pin.messenger.conference-ended.v1 | crm, notifications, dev-stats | Групповой звонок сделки завершён → карточка в таймлайн сделки + аналитика | conference-ended.md |
| Produced | CallRecordingAvailableEvent | pin.messenger.call-recording-available.v1 | crm, notifications | Запись звонка готова (file storage FileId) → прикрепить к сделке + уведомить участников | call-recording-available.md |
Ключи партиционирования
| Topic | Partitions | Kafka key | Порядок |
|---|---|---|---|
deal-chat-status-changed.v1 | 3 | DealId | per-deal смены статуса |
message-posted.v1 | 6 | ChatId | per-chat порядок сообщений |
contact-request-created.v1 | 3 | ContactId | per-связь (created → accepted) |
contact-request-accepted.v1 | 3 | ContactId | per-связь (created → accepted) |
conference-ended.v1 | 3 | DealId | per-deal конференции |
call-recording-available.v1 | 3 | SessionId | per-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-ended→crm+notifications+dev-stats;call-recording-available→crm+notifications), они независимы: падение одного не откатывает других; редоставка повторяет всех, идемпотентность гасит дубли.
Наблюдаемость пайплайна событий
- Метрики: размер и возраст outbox-backlog (алерт
> 1000/ lag> 5 мин, README); лаг консюмеровcrm/identity; счётчик сообщений в DLQ по топику; publish-rate по каждому produced-топику. - Логи публикации/потребления несут только id/enum +
traceparent(корреляция сквозная); тела сообщений, ФИО/телефон, IP/UA в события и логи не попадают (INV-MSG-6).
Потребляемые события (consumed)
Дом контрактов — сервисы-источники; здесь только карта (consumer-доки — ../consumers/).
| Type | Topic | Источник | Consumer-эффект | Consumer-док |
|---|---|---|---|---|
| Consumed | pin.crm.deal-created.v1 | crm | Провизия Deal-чата + участники → deal-chat-status-changed | deal-created.md |
| Consumed | pin.crm.deal-stage-changed.v1 | crm | Инфо-карточка этапа; терминальный ToStatus → закрытие чата | deal-stage-changed.md |
| Consumed | pin.crm.deal-members-changed.v1 | crm | Set-реконсиляция состава чата (INV-CHAT-6) | deal-members-changed.md |
| Consumed | pin.crm.document-requested.v1 | crm | Карточка целевого действия «Загрузите документы» (Pending) | document-requested.md |
| Consumed | pin.crm.booking-paid.v1 | crm | Инфо-карточка «Договор бронирования оплачен» | booking-paid.md |
| Consumed | pin.crm.booking-expired.v1 | crm | Карточка «Бронь истекла — подтвердите/продлите» | booking-expired.md |
| Consumed | pin.identity.users.v1 | identity | Реплика ФИО/телефона (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).
Связанные документы
- Контракты (produced): deal-chat-status-changed.md, message-posted.md, contact-request-created.md, contact-request-accepted.md, conference-ended.md, call-recording-available.md.
- Домены-источники payload: chat.md, message.md, contact.md; карта сервиса — ../README.md.
- Потребители: crm/consumers/deal-chat-status.md,
notifications(консюмеры уведомлений); источники consumed — crm, identity. - ADR: 0055 мини-мессенджер, 0056 simplicity-first.