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

События контекста selections: карта топиков и отклонённые события

Индекс интеграционных событий pin.selections.* + дом решения о непубликуемых событиях (snapshot-created). Минимальность — ADR-0056: каждый топик существует только при живом потребителе, которому синхронный API не подходит.

Metadata

ПолеЗначение
Owning teamPIN Engagement
Статус документаdraft
Область действияконтекст selections, папка events/
Assumptionsconsumer item-added активны: user-activityconsumers/selections-events.md, dev-statsconsumers/selection-item-added.md; consumer created/updated/deleted (user-activity) — будущий путь docs/06-services/user-activity/consumers/selections-actions.md
Change contextgreenfield; фиксирует список Produced events из ../README.md

Публикуемые события (по одному контракт-доку на топик)

СобытиеTopicProducer-операцииConsumerКонтракт
SelectionCreatedKafkaEventpin.selections.selection-created.v1POST /selections, POST /selections/from-favoritesuser-activity (ActionEvent → карточка лида CRM)selection-created.md
SelectionUpdatedKafkaEventpin.selections.selection-updated.v1PUT /selections/{id}, PUT /selections/{id}/itemsuser-activityselection-updated.md
SelectionItemAddedKafkaEventpin.selections.selection-item-added.v1PUT /selections/{id}/items (per новый объект недвижимости в диффе), POST /selections/from-favorites (per скопированный объект недвижимости)dev-stats (этап Selections воронки), user-activity (AddToSelection)selection-item-added.md
SelectionDeletedKafkaEventpin.selections.selection-deleted.v1DELETE /selections/{id} (soft)user-activityselection-deleted.md
SelectionSharedKafkaEventpin.selections.selection-shared.v1POST /selections/{id}/share (агент отправил подборку клиенту)crm (лента лида SelectionAttached), user-activityselection-shared.md

Все пять — через транзакционный outbox атомарно с мутацией/операцией агрегата; ключ = SelectionId; ContractVersion = 1. Порядок между топиками не гарантирован — потребитель упорядочивает по OccurredAt (в пределах одной подборки поэлементные item-added упорядочивает Version; правило в каждом контракте). selection-item-addedпоэлементное (одно событие на один добавленный объект недвижимости), а не агрегатное: set-items-батч из N новых объектов даёт N событий (обоснование per-item vs batch — в контракте, раздел «Батч и гранулярность»).

Доставка, идемпотентность и обработка отказов (общее для всех топиков)

Единая политика для пяти топиков; частные ключи идемпотентности и DLQ-условия — в каждом контракте.

  • Доставка — at-least-once через транзакционный outbox: пока мутация агрегата не зафиксирована, событие потребителям не уходит; после фиксации доставляется гарантированно. Застрявший/отстающий outbox — alert outbox-lag > 5 мин (../README.md, Alerts); события не теряются, догоняют после разбора.
  • Идемпотентность потребителя — дедуп по MessageId; дополнительно по бизнес-ключу: воронка dev-stats — по SelectionItemId (uniqState, порядку не чувствительна), crm и user-activity — по SourceMessageId. Повторная доставка того же события → skip без побочных эффектов.
  • Порядок — внутри одной подборки гарантирован ключом SelectionId (все события подборки в одной партиции); между топиками не гарантирован — потребитель упорядочивает по OccurredAt (поэлементные item-added — по Version). Проекции upsert-семантичны и терпят updated/deleted, пришедшие раньше created.
  • Транзиентный сбой потребителя → повторная доставка до успешного ack (порядок в партиции сохраняется). Неустранимая ошибка (напр. неизвестное значение Action в selection-sharedCRM_SELECTION_UNKNOWN_ACTION) → non-retriable DLQ + alert; потери нет (топики append-only, реплей возможен).
  • Отставание потребителя источник не блокирует: подборка и её публичное/internal чтение — синхронный API (source of truth); события лишь наполняют ленты действий, воронку и ленту лида — согласованность eventual, окно = SLA соответствующего консюмера (< 1 мин user-activity, < 5 мин dev-stats/crm).

Отклонённые события (решение, не пробел)

pin.selections.selection-shared.v1 — пересмотрено, публикуется

Ранее отклонялось: у «шаринга» не было backend-операции в selections (SharePopover копирует ссылку на клиенте); клик «поделиться» и просмотры по публичной ссылке — зона user-activity; привязка к лиду — данные crm. Клауза «Пересмотр» сработала: появилась серверная операция «агент отправляет подборку клиенту» POST /selections/{id}/share (UC-SEL-005) с двумя потребителями, которым синхронный API не подходит (crm — лента лида SelectionAttached по паре agent↔investor, которую selections не резолвит; user-activity — ActionEvent агента). Событие вынесено в «Публикуемые события» выше; контракт — selection-shared.md.

Клиентским осталось (событие selection-shared его не покрывает): шаринг публичной ссылки /collection/{publicCode} через SharePopover (копирование на фронте) и просмотры по ней — трекает user-activity (ADR-0054); отправка подборки в сделку — отдельная операция crm send-deal-selection.md (таймлайн сделки, без Kafka).

pin.selections.snapshot-created.v1 — не публикуется

Кандидат: факт снятия иммутабельной копии подборки для блока поста.

#Причина отклоненияДетали
1Единственный потребитель взаимодействует синхронноnews вызывает POST /internal/selections/{id}/snapshots (m2m) и получает SnapshotId в ответе — событие ему ничего не добавит (../domain/selection-snapshot.md, раздел «Доменные и интеграционные события»).
2Snapshot — техническая копия, не действие пользователяВ историю действий владельца подборки попадает публикация поста (зона news), а не внутренняя механика копирования; ActionEvent из snapshot-created был бы шумом.
3Консистентность держится на синхронном ответеnews фиксирует SnapshotId в typed-jsonb блоке поста в своей транзакции; событийная связка добавила бы окно рассинхрона.

Пересмотр: второй потребитель snapshot-ов (например, dev-stats для статистики публикаций) — повод добавить топик additive, как уже зафиксировано в ../domain/selection-snapshot.md.

Consumed-события: нет

Контекст ничего не потребляет из Kafka: состав from-favorites — синхронный internal API favorites; snapshot-ы — синхронные вызовы news; чтение для CRM — синхронный internal API (../README.md, Ownership). Кандидаты-консюмеры не рассматривались — входящих событийных потребностей у домена нет (подборка не реагирует на изменения объектов недвижимости/пользователей: недоступные объекты недвижимости фильтрует выдача каталога, INV-SEL-008).

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