События сервиса support: карта топиков и решения об интеграциях
Индекс интеграционных событий pin.support.* (по одному контракт-доку на топик) + дом решений об
интеграциях контекста «Служба заботы»: что публикуется, что потребляется и что сознательно не
публикуется. Минимальность — ADR-0056:
топик существует только при живом потребителе, которому синхронный API не подходит. Единственный внешний
потребитель produced-событий support — notifications (оповещения операторов/заявителя/сторон).
Metadata
| Поле | Значение |
|---|---|
| Owning team | PIN Platform (Care squad) |
| Статус документа | draft |
| Область действия | контекст support, папка events/ (produced + consumed + отклонённые) |
| Зависимости | Kafka (outbox producer + консюмер identity), identity (upstream-топик реплики заявителя), notifications (потребитель produced-событий) |
| Change context | greenfield; фиксирует Produced/Consumed из ../README.md («Events И Consumers») |
Метаданные и общие правила
Каждое событие несёт метаданные: MessageId (Guid v7, для дедупликации), CreatedAt, версию контракта,
типизированный payload, AggregateId, AggregateType; сквозной trace восстанавливается из метаданных. Все
produced-топики публикуются через транзакционный outbox атомарно с мутацией агрегата: пока изменение не
зафиксировано, событие не отправляется потребителям. У всех ContractVersion = 1 и NumberOfPartitions = 3
(низкий объём тикетов/дел, 1–10k активных, ADR-0056). Прямого PII payload'ы не несут по построению —
ФИО/телефон/email потребитель (notifications) резолвит из собственной реплики identity; события несут
только id/enum/метки времени.
Публикуемые события (по одному контракт-доку на топик)
| Событие | Topic | Ключ (AggregateId) | Producer-операция | Consumers | Контракт |
|---|---|---|---|---|---|
TicketCreatedKafkaEvent | pin.support.ticket-created.v1 | TicketId | create-ticket (веб-форма) / create-ticket-on-behalf (оператор) → New | notifications (оповестить дежурных операторов очереди) | ticket-created.md |
TicketMessagePostedKafkaEvent | pin.support.ticket-message-posted.v1 | TicketId | reply-ticket (публичный ответ оператора, Visibility = Public) | notifications (уведомить заявителя о новом ответе, 4.1/4.5) | ticket-message-posted.md |
TicketStatusChangedKafkaEvent | pin.support.ticket-status-changed.v1 | TicketId | take/assign/change-ticket-status (take/resolve/close/reopen) | notifications (информировать заявителя, 4.5/4.6) | ticket-status-changed.md |
ArbitrageResolvedKafkaEvent | pin.support.arbitrage-resolved.v1 | CaseId | resolve/escalate-arbitrage-case (решение 6.3 / эскалация 6.5) | notifications (информировать стороны, 6.4) | arbitrage-resolved.md |
Дом полей payload — доменные агрегаты (ticket.md /
arbitrage-case.md, раздел «Доменные и интеграционные события»); контракт-доки
несут типизированную проекцию (дельту), а не второй дом. Ключ = AggregateId даёт per-ticket / per-case
порядок переходов (одна партиция на ключ).
Событие worker-а SLA (additive, дом — в background-доке)
ticket-sla-monitor при обнаружении просрочки первого ответа/решения (флаг SlaBreached) публикует
TicketSlaBreachedKafkaEvent (pin.support.ticket-sla-breached.v1, AggregateId = TicketId)
через тот же транзакционный outbox — консюмер notifications (эскалация просрочки супервизору/дежурным).
Полный payload и решение об этом топике инлайнятся в доме worker-а
(../background/ticket-sla-monitor.md, шаг 6) — как у CRM
timeline-overdue (../../crm/background/timeline-overdue.md); это
additive-расширение реестра produced-событий (отдельный контракт-док — по мере стабилизации).
Потребляемые события (consumed)
Display-реплика подписи заявителя из identity для карточек тикетов/арбитража без джойна (дом полей —
ticket.md / arbitrage-case.md). Мутация реплики и
отметка идемпотентности выполняются в одном согласованном изменении.
| Топик (upstream) | Событие | Consumer-док | Side effect | Идемпотентность |
|---|---|---|---|---|
pin.identity.users.v1 | UserUpsertedKafkaEvent | ../consumers/identity-user-upserted.md | Upsert RequesterName/RequesterPhone на всех тикетах (RequesterUserId) и ComplainantName/ComplainantPhone на арбитражных делах (ComplainantUserId) заявителя (fan-out) | MessageId + монотонный version-guard (IdentityRequesterVersion/IdentityComplainantVersion) |
Контракт источника — ../../identity/events/user-upserted.md.
UserBlockedKafkaEvent того же топика консюмер игнорирует (статус заявителя на display тикета не влияет).
Собственные интеграционные события реплики не публикуют (внутренние данные, ADR-0056).
Гарантии доставки, порядка и восстановления
- Доставка. Produced-события — at-least-once через транзакционный outbox: публикуются атомарно с мутацией
агрегата (пока изменение не зафиксировано — событие не уходит), потребитель обязан дедуплицировать по
MessageId(first-seen). Повторная доставка (дубли) — штатное поведение at-least-once, не ошибка. - Порядок внутри топика. Ключ =
AggregateId(TicketId/CaseId) даёт per-ticket / per-case порядок переходов в пределах одного топика (одна партиция на ключ). - Порядок между топиками не гарантирован.
ticket-created,ticket-status-changed,ticket-message-postedиticket-sla-breached— разные топики; общий ключTicketIdколокализует сообщения только внутри своего топика, но межтопикового порядка не даёт. Потребитель (notifications) не должен полагаться на то, чтоticket-createdпридёт раньшеticket-status-changedтого же тикета: при расхождении — обрабатывать факт как самодостаточный (каждое событие несётNumber/Status), а недостающую подпись/детали дорезолвивать из собственных реплик/по API. То же дляarbitrage-resolvedотносительно внутренних доменных фактов дела. - Retry / DLQ (потребление). Обработка produced-событий и consumed-реплики
identity— с ретраями транзиентных сбоев; исчерпание попыток → карантин (DLQ) с алертом, без блокировки партиции. Для репликиidentityустаревшее/out-of-order событие не ретраится, а штатно игнорируется version-guard-ом (IdentityRequesterVersion/IdentityComplainantVersion) — это не ошибка. Дом ретраев/DLQ консюмера — ../consumers/identity-user-upserted.md. - Промежуточное состояние для пользователя. Между коммитом операции и доставкой уведомления есть окно (SLA потребления < 1 мин): заявитель уже видит новый статус/ответ в карточке тикета (source of truth PostgreSQL), уведомление приходит следом. Отставание поисковой проекции описано в ../search/tickets.md / ../search/kb.md.
Отклонённые события/консюмеры (решение, не пробел)
pin.support.arbitrage-registered / ticket-assigned / queue-* — НЕ публикуются
| Кандидат | Причина отклонения |
|---|---|
arbitrage-registered | Регистрация дела (6.1) не имеет внешнего потребителя: информирование сторон нужно только по терминальному исходу (arbitrage-resolved, 6.4); внутренние оповещения арбитра — доменное событие той же операции. |
ticket-assigned | Назначение оператора — внутренний факт support; смену статуса при взятии несёт ticket-status-changed, а оповещение оператора — доменное событие/дежурная модель notifications. Внешнего потребителя нет. |
ticket-message-added (заметки/ответ заявителя) | Внутренние заметки (Visibility = Internal) и сообщения самого заявителя (add-ticket-message) внешнего потребителя не имеют. Исключение: публичный ответ оператора (reply-ticket, Visibility = Public) публикуется как ticket-message-posted (уведомление заявителя, см. таблицу produced выше) — внешний потребитель notifications есть. |
queue-created / queue-archived | TicketQueueEntity — внутренний справочник маршрутизации (ticket-queue.md); внешние контексты о нём не знают. Только доменные события (QueueCreated/QueueArchived), без Kafka. |
Сквозной просмотр / ручные вмешательства (2.x/3.x/5.x) — синхронно, без событий
Просмотр пользователей/сделок/финансов и ручные интервенции support делает синхронно через
platform-scope API чужих сервисов (identity/crm/chessboard/rewards), а не событиями (README, «Не
делает»). Факт вмешательства фиксируется ссылкой в ArbitrageNoteEntity.AuditRefs, не Kafka-топиком.
Пересмотр любого пункта — additive при появлении реального событийного потребителя.
Безопасность
| Поток | Класс данных | Правило |
|---|---|---|
ticket-created / ticket-message-posted / ticket-status-changed / arbitrage-resolved payload | Internal | include (только id/enum/метки времени; ФИО/телефон/email/тексты сообщений исключены — exclude) |
consumed identity-реплика | PII (ФИО/телефон заявителя) | приходит из доверенного топика identity (m2m ACL); в репликах support — display-подпись карточки; в логах ФИО/телефон маскируются (И***, +7***1234) |
Actor/permission — на стороне producer-операций (support.tickets.*/support.arbitrage.*); консюмер —
system-actor (platform-scope). События НЕ содержат secrets, tokens, raw PII/платёжных реквизитов, тел
переписки/обоснований, unbounded-вложений.
Связанные документы
- Контракты: ticket-created.md, ticket-message-posted.md, ticket-status-changed.md, arbitrage-resolved.md; консюмер: ../consumers/identity-user-upserted.md; worker — ../background/ticket-sla-monitor.md.
- Дома полей: ../domain/ticket.md, ../domain/arbitrage-case.md, ../domain/ticket-queue.md.
- Границы/потоки: ../README.md; решения: ADR-0056 (минимальность топиков).