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

Event Contract: pin.referral.bond-created (ReferralBondCreatedKafkaEvent)

Версионируемый контракт интеграционного Kafka-события «реферальная связь создана». Consumer-документы ссылаются сюда, не переописывая payload. Дом полей агрегата — referral-bond.md; здесь перечислено только то, что реально входит в событие.

Назначение

Фиксирует создание ReferralBondEntity ([*] → Active) — факта «кто привёл кого, когда и через какую ссылку» по правилам привязки (attach-rules.md, правила R1/R2/R6). Даёт downstream-контекстам основание без синхронного запроса в referral: rewards строит реплику связи и будущие начисления вознаграждений по BondId (решение № 7 README.md: referral — факт связи, rewards — деньги), notifications отправляет рефереру СМС «по вашей ссылке зарегистрировался пользователь», messenger создаёт контакт «агент ↔ инвестор» (поток «Реферальная привязка», service-boundaries.md).

Metadata

ПолеЗначение
Event typepin.referral.bond-created
C# typeReferralBondCreatedKafkaEvent
Topicpin.referral.bonds.v1 (общий с ReferralBondClosedKafkaEvent — общий key = строгий порядок событий per-приглашённый)
Partitions / key6 partitions (~1 млн связей на горизонте, ADR-0056) / key = ReferredUserId
ContractVersion1
Producerreferral: consumer T1 user-upserted.md (правило R1), consumer T2 user-signed-in.md (правило R2), admin-коррекция rebind-bond.md (правило R6)
Статусdraft

Метаданные события: идентификатор сообщения (Guid v7, дедупликация), время, версия (= ContractVersion), AggregateId = BondId, AggregateType = "ReferralBond". Корреляция трейса восстанавливается из метаданных. Публикуется атомарно с созданием связи и журнальной записи.

Payload ReferralBondCreatedKafkaEvent

Snapshot-поля ссылаются на дом ReferralBondEntity (раздел «Доменные и интеграционные события») и ReferralLinkEntity (Channel).

ПолеТипОбязательностьИсточник (поле агрегата)CompatibilityОписание
BondIdGuidДаReferralBondEntity.IdImmutableИдентификатор связи; на него ссылаются начисления rewards (= AggregateId).
ReferredUserIdGuidДаReferralBondEntity.ReferredUserIdImmutableПриглашённый пользователь («кого привели»); = Kafka key.
ReferrerUserIdGuidДаReferralBondEntity.ReferrerUserIdImmutableРеферер («кто привёл») — владелец ссылки на момент привязки; получатель СМС notifications и участник контакта messenger.
ReferrerCompanyIdGuid?НетReferralBondEntity.TenantIdImmutableCompanyId реферера на момент привязки; null — реферер-инвестор (ADR-0052).
ReferralLinkIdGuidДаReferralBondEntity.ReferralLinkIdImmutableСсылка, через которую произошла привязка.
Channelstring (enum ReferralChannel)ДаReferralLinkEntity.Channel на момент привязкиAdditive only (новые значения)Канал распространения ссылки (Direct/Telegram/…, дом enum — referral-link.md); для аналитики каналов у потребителей.
BoundAtDateTimeOffsetДаReferralBondEntity.BoundAtImmutableМомент привязки (= время триггер-события identity; для T3/R6 — момент операции).
AppliedRulestring (enum ReferralAttachRule)ДаReferralBondEntity.AppliedRuleAdditive only (новые значения)Правило, создавшее связь: R1 (регистрация), R2 (авторизация в окне lazy-регистрации), R6 (admin-коррекция). Дом enum — attach-rules.md.
VersionlongДаReferralBondEntity.VersionImmutable semanticsМонотонная доменная версия связи; guard потребителей (события created/closed одного топика упорядочены key-ом).

Вложенных моделей нет — все поля скалярные. ФИО/телефон участников в payload не входят (PII-минимизация): notifications и messenger берут отображаемые данные из собственных реплик pin.identity.users.v1.

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

УсловиеПереход состоянияСогласованность
Правило R1 — регистрация с реф-ссылкой (consumer T1 user-upserted.md)ReferralBondEntity: [*] → Activeсвязь + журнал Initial + событие — в одном согласованном изменении
Правило R2 — авторизация в окне lazy-регистрации (consumer T2 user-signed-in.md)та жета же
Правило R6 — admin-rebind (rebind-bond.md): создание связи-замены[*] → Active (новая) одновременно со Active → Replaced (старая, INV-REF-B4)новая связь + замена старой + журнал Rebind + оба события (created + closed) — в одном согласованном изменении

Правила R3/R4 связь не создают — событие не публикуется (уходит только lead-attach-requested.md). Закрытие связи (Replaced/Revoked) — отдельный контракт ReferralBondClosedKafkaEvent того же топика (bond-closed.md).

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

Сверено с referral-bond.md («Потребители pin.referral.bonds.v1») и service-boundaries.md.

Consumer-контекстЭффект (событие → что меняется)ИдемпотентностьЗадержка (SLA)
rewardsРеплика связи — основание будущих реферальных начислений по BondId (consumer — referral-bond.md)идентификатор сообщения + BondId (upsert реплики) + Version-guard< 5 мин
notificationsСМС/уведомление рефереру (ReferrerUserId) «по вашей ссылке зарегистрировался пользователь»идентификатор сообщения (повтор — no-op, СМС не дублируется)< 1 мин (SLO привязки, README.md)
messengerКонтакт «реферер ↔ приглашённый» для будущего чата (ADR-0055)идентификатор сообщения + business key (ReferrerUserId, ReferredUserId)< 5 мин

crm этот контракт не потребляет: карточка лида создаётся по отдельному событию lead-attach-requested.md (лид возможен и без связи — правила R3/R4).

Межтопиковый порядок не гарантируется: ReferralBondCreatedKafkaEvent (pin.referral.bonds.v1) и ReferralLeadAttachRequestedKafkaEvent (pin.referral.lead-attachments.v1) публикуются в одном коммите, но в разные топики — взаимного порядка «связь vs лид» нет, потребители независимы. Окно согласованности: связь существует в referral сразу (тот же коммит), поэтому getMyReferrer приглашённого возвращает реферера немедленно после обработки триггер-события; downstream отстаёт — СМС рефереру < 1 мин, реплика связи в rewards и контакт в messenger < 5 мин.

Гарантии, на которые может опереться потребитель:

  • ReferredUserId != ReferrerUserId всегда (INV-REF-B2): событие никогда не публикуется для самопривязки — отдельная проверка «сам себе» потребителю не нужна.
  • На один BondId приходит ровно одно created; последующее закрытие — отдельное closed (bond-closed.md) того же топика и ключа, строго после created.
  • Потребители независимы и идемпотентны: сбой/повтор одного (rewards/notifications/messenger) не влияет на других; дубль по идентификатору сообщения — no-op (СМС не дублируется, реплика связи и контакт апсертятся).

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

  • Версия — один ContractVersion = 1; при несовпадении версии потребитель логирует warning и продолжает (upcasting/schema-migration нет).
  • Additive-only для minor: новые optional-поля с безопасным default — без bump (пример уже в контракте: Channel/AppliedRule заведены с первой версии, чтобы не расширять позже).
  • Breaking (удаление/переименование/смена типа) — ContractVersion = 2 + новый топик pin.referral.bonds.v2 + dual-publish на период миграции + deadline миграции consumers + sunset .v1.
  • Unknown enum Channel → fallback Other + warning; unknown AppliedRule → warning + обработка как R1 (консервативно, правило attach-rules.md).
  • Порядок per-ReferredUserId гарантирован key-ом; потребители обязаны держать Version-guard — события created и closed одного топика упорядочены между собой.

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

ПолеКласс данныхПравило
BondId, ReferredUserId, ReferrerUserId, ReferrerCompanyId, ReferralLinkId, VersionInternalinclude
Channel, AppliedRule, BoundAtInternalinclude

Событие НЕ содержит secrets, tokens, PII (ФИО/телефон/email), сумм вознаграждений (денег в referral нет — зона rewards), slug ссылки (потребителям не нужен; резолв — по ReferralLinkId). Доступ к топику — m2m ACL Kafka (rewards/notifications/messenger).

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

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

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