Event Contract: pin.crm.lead-created (LeadCreatedEvent)
Версионируемый контракт интеграционного события «Лид создан». Consumer-документы ссылаются сюда, не
переописывая payload. Дом полей агрегата — entity: LeadEntity; здесь перечислено
только то, что реально летит в событии.
Назначение
Фиксирует появление первой активной связи «инвестор ↔ агент» — создание LeadEntity (глоссарий «Лид»).
Публичное: даёт downstream-контекстам факт нового лида без синхронного запроса в crm. Потребители:
notifications — уведомляет текущего исполнителя (ExecutorAgentUserId) о новом лиде; referral — подтверждает, что
запрошенная реферальным приходом карточка лида создана (закрытие цикла
lead-attach-requested → журнал регистраций). Событие НЕ
несёт ФИО/телефон: клиентская база агентства — внутренние данные (застройщик о лиде не знает), consumers
берут display-данные из собственных реплик pin.identity.users.v1.
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.crm.lead-created |
| Topic | pin.crm.lead-created.v1 |
| Partitions / key | 6 (ADR-0056) / key = LeadId (= AggregateId) |
| ContractVersion | 1 |
| Producer | crm: лид-заявки (Source = ConsultationRequest/BookingRequest), обработка реферальной привязки (Source = Referral), автосоздание лида при создании фиксации / ручном добавлении (Source = Manual) |
| Статус | draft |
Событие публикуется атомарно со сменой состояния агрегата: пока создание лида не зафиксировано, событие
не отправляется потребителям; после фиксации — доставляется гарантированно (at-least-once), ключ = LeadId.
Payload LeadCreatedEvent
Все поля — снимок LeadEntity на момент создания. Дом типов/ограничений — lead.md
(раздел «Поля», «Доменные и интеграционные события»); PII-полей нет.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
LeadId | Guid | Да | LeadEntity.Id | Immutable | Ключ агрегата (= Kafka key, = AggregateId). |
TenantId | Guid | Да | LeadEntity.TenantId | Immutable | Tenant boundary = AgencyCompanyId (для лидов «на ПИН», UC-2 — компания ПИН; ADR-0052). |
ExecutorAgentUserId | Guid | Да | LeadEntity.ExecutorAgentUserId | Additive only | Текущий участник-исполнитель (активный Executor) пары — получатель уведомления notifications. |
OriginAgentUserId | Guid | Да | LeadEntity.OriginAgentUserId | Immutable | Агент, изначально создавший лид: иммутабельная атрибуция (воронка агентства / антифрод). |
PrimaryInvestorUserId | Guid | Да | LeadEntity.PrimaryInvestorUserId | Immutable | Основной инвестор-участник пары (корреляция реф-привязки). |
ClientId | Guid | Да | LeadEntity.ClientId | Immutable | Карточка клиента (client.md). |
Source | string (enum LeadSource) | Да | LeadEntity.Source | Additive only (новые значения) | Как появился лид (см. enum ниже). Иммутабельно. |
ReferralLinkId | Guid? | Нет | LeadEntity.ReferralLinkId | Immutable | Реферальная ссылка-источник; null — лид не из реф-привязки. Заполнен ⇔ Source = Referral. |
CreatedAt | DateTimeOffset | Да | LeadEntity.CreatedAt | Immutable | Момент создания лида. |
ExpiresAt | DateTimeOffset | Да | LeadEntity.ExpiresAt | Additive only | Срок жизни лида (CreatedAt + 1 месяц); продление меняет поле, но событие несёт значение на момент создания. |
Enum LeadSource (string)
Дом — lead.md; в событии — строкой. Значения: ConsultationRequest (заявка на
консультацию/презентацию, UC-1/UC-2), BookingRequest (заявка на бронирование, UC-3), Referral
(реферальная привязка), Manual (агент создал лид вручную / автосоздание при фиксации).
Вложенных моделей нет — все поля скалярные. ФИО/телефон/Wishes в payload не входят (правило PII ниже).
Когда публикуется
Единственное срабатывание на лид — при переходе [*] → Active (INV-CRM-001: у пары нет активного лида;
иначе создание — идемпотентный no-op, событие не публикуется).
| Условие | Переход состояния | Публикация |
|---|---|---|
Публичная лид-заявка «консультация/презентация» (Source = ConsultationRequest) | [*] → Active | атомарно со сменой состояния агрегата |
Публичная заявка на бронирование объекта (Source = BookingRequest) | [*] → Active | та же |
Обработка реферальной привязки по pin.referral.lead-attach-requested.v1 (Source = Referral, ReferralLinkId заполнен) | [*] → Active | та же |
Автосоздание лида при создании заявки на фиксацию / ручном добавлении, если нет активного лида пары (Source = Manual) | [*] → Active | та же |
Реактивация Expired → Active (Extend) отдельным событием lead-created не сопровождается — это не
новый лид. Истечение срока публикуется lead-expired.
Потребители (consumers)
| Consumer-контекст | Side effects | Идемпотентность | Задержка (SLA) |
|---|---|---|---|
notifications | Уведомление исполнителю (ExecutorAgentUserId) «новый лид» (push/in-app/СМС); получатель и ФИО клиента резолвятся из собственных реплик, не из payload | MessageId | < 5 мин |
referral | Подтверждение привязки: отметка в журнале регистраций (флаг LeadRequested → подтверждён) корреляцией по ReferralLinkId + PrimaryInvestorUserId; при Source != Referral / ReferralLinkId = null — no-op | MessageId + бизнес-ключ (ReferralLinkId,PrimaryInvestorUserId) | < 5 мин |
Кросс-сервисные контракты
- Ожидает (upstream). Для
Source = Referralкарточка лида создаётся в ответ наpin.referral.lead-attach-requested.v1:crmожидает в нёмReferralLinkIdиInvestorUserId, по которым лид корреллируется с журналом привязки. Отсутствие/ устаревание этих полей → лид не создаётся по реф-пути (обрабатывается другойSource). - Предоставляет (downstream). Публикует
lead-createdс ключом партиционированияLeadId,ContractVersion = 1. Для закрытия реф-цикла гарантирует наличиеReferralLinkId+PrimaryInvestorUserIdприSource = Referral, по которымreferralподтверждает привязку (флагLeadRequested). ДляSource != ReferralReferralLinkId = null, иreferralтрактует событие как no-op.
Доставка, порядок и обработка сбоев
- Гарантия публикации. Событие уходит только после фиксации перехода
[*] → Active; при откате не публикуется. Доставка — at-least-once, ключLeadId. - Порядок. События одного лида упорядочены ключом
LeadId. Событие — append-only факт создания, version-guard потребителю не нужен. - Идемпотентность потребителя.
notifications— дедуп поMessageId;referral— поMessageId+ business key (ReferralLinkId,PrimaryInvestorUserId) (повторная доставка не задваивает флаг журнала). На уровне продюсера: у пары без активного лида — идемпотентность создания (INV-CRM-001: повтор — no-op без события). - Retry. Транзиентный сбой потребителя → повтор обработки с backoff; событие доступно до успешной обработки.
- DLQ. Неустранимый сбой после исчерпания попыток → dead-letter c алертом; проигрывание безопасно
(дедуп/business key). Неизвестный
LeadSource— уведомление/подтверждение всё равно обрабатываются (fail-safe), в DLQ не уходит.
Версионирование и совместимость
- Версия — один
ContractVersion = 1; при несовпадении версии контракта потребитель логирует предупреждение и продолжает обработку. Топик кодирует версию суффиксом.v1по конвенции. - Additive-only для minor: новые поля добавляются как optional с безопасным default — без bump.
- Breaking (удаление/переименование/смена типа/смысла) — новый
ContractVersion = 2+ новый топикpin.crm.lead-created.v2+ dual-publish на период миграции + deadline миграции consumers + sunset.v1. - Unknown enum
LeadSource— fallback: сохранить строку как есть + warning (карточка/уведомление всё равно обрабатываются). Событие — факт создания (append-only), version-guard потребителю не нужен, только дедуп поMessageId(+ бизнес-ключ уreferral).
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
LeadId, TenantId, OriginAgentUserId, ExecutorAgentUserId, PrimaryInvestorUserId, ClientId | Internal | include |
Source, ReferralLinkId, CreatedAt, ExpiresAt | Internal | include |
ФИО и телефоны клиента/инвестора в payload не передаются (клиентская база — внутренние данные агентства).
Consumers, которым нужны display-данные, берут их из собственной реплики pin.identity.users.v1 и в логах
маскируют телефон +7***1234, ФИО — по правилу PII owning-домена.
- Доступ к топику
pin.crm.lead-created.v1— m2m ACL Kafka (толькоnotifications,referral). - Tenant-правило (ADR-0052): лид принадлежит
TenantId = AgencyCompanyId; consumers не раскрывают лид другим компаниям. Событие НЕ содержит secrets, tokens, платёжных реквизитов,Wishes, unbounded вложений.
Наблюдаемость
- Логи (без PII): факт публикации
lead-createdсLeadId/TenantId/ExecutorAgentUserId/OriginAgentUserId/Source/ReferralLinkId(идентификаторы, не PII); ФИО/телефон клиента в payload и логах отсутствуют. - Метрики: число созданных лидов по топику в разрезе
Source; сквозная задержка доnotifications/referral(< 5 мин); доля реф-привязок, подтверждённыхreferral(закрытие цикла); глубина DLQ (порог алерта > 0); доля повторных доставок. - Доступ: топик — m2m ACL Kafka (
notifications/referral); лид не раскрывается другим компаниям (TenantId =AgencyCompanyId);Wishesв событие не входит.
Acceptance Criteria
- Given у пары «инвестор ↔ агент» нет активного лида (INV-CRM-001), When приходит лид-заявка
(
ConsultationRequest/BookingRequest) / реф-привязка / ручное добавление, Then переход[*] → Active, ровно одноlead-createdпубликуется с соответствующимSource. - Given
Source = Referral, When событие обработано, ThenReferralLinkIdзаполнен, иreferralвыставляет флагLeadRequestedподтверждённым (корреляция поReferralLinkId+PrimaryInvestorUserId). - Given у пары уже есть активный лид, When повторная попытка создания, Then это идемпотентный no-op — событие не публикуется.
- Given реактивация
Expired → Active(Extend), When лид продлён, Thenlead-createdне публикуется (это не новый лид). - Given повторная доставка события, When
referralобрабатывает, Then флаг журнала выставляется один раз (business keyReferralLinkId,PrimaryInvestorUserId). - Given неизвестное значение
LeadSource, When потребитель обрабатывает, Then строка сохраняется как есть + warning (уведомление/подтверждение всё равно обрабатываются). - Given breaking-изменение payload, When новый контракт, Then
pin.crm.lead-created.v2+ dual-publish + sunset.v1.
Обратные ссылки
Автоссылки: где используется этот документ.
- API (10): GET /api/agent/crm/leads — Список лидов агента, GET /api/investor/crm/leads — Мои лиды (инвестор), GET /api/pin-manager/crm/leads — Сквозной список всех лидов, GET /api/pin-manager/crm/leads/{leadId} — Карточка любого лида (Менеджер ПИН), POST /api/agent/crm/clients — Создать клиента вручную, POST /api/agent/crm/leads — Создать лид вручную, POST /api/agent/crm/leads/{leadId}/archive — Архивировать лид, POST /api/agent/crm/leads/{leadId}/extend — Продлить лид, POST /api/public/crm/booking-requests — Заявка на бронирование объекта (витрина), POST /api/public/crm/consultation-requests — Заявка на консультацию/презентацию
- Use Cases (2): UC-BKG-001. Инвестор инициирует бронирование с витрины, UC-CRM-001. Создание лида (заявка / реферал / вручную)
- События (3): Event Contract: pin.crm.lead-booking-intent-changed (LeadBookingIntentChangedEvent), Event Contract: pin.crm.lead-expired (LeadExpiredEvent), Event Contract: pin.crm.lead-participants-changed (LeadParticipantsChangedEvent)
- Консюмеры (13): Consumer: контроль смены телефона связанного инвестора (pin-investor-owner-identity-users), Consumer: Calendar Identity User Sync (calendar-identity-users), Consumer: CRM Calendar Appointments Sync (crm-calendar-appointments), Consumer: CRM Identity User Sync (CrmUserUpsertedConsumer), Consumer: DevStatsFunnelLeadCreatedConsumer, Consumer: Identity Events → Notifications (notifications-identity-events), Consumer: identity-users-sync (ProfileUserUpsertedConsumer, ProfileUserBlockedConsumer), Consumer: identity.users.v1 → AuthorReplica (news-identity-users), Consumer: Messenger User Replica Sync (MessengerUserUpsertedConsumer), Consumer: ReferralRegisteredConsumer (pin.referral.lead-attachments.v1), Consumer: ReferralUserUpsertedConsumer (pin.identity.users.v1), Consumer: Support Identity User Sync (+1)
- scenarios (4): UC-OVR-001. Запрос консультации у агента недвижимости, UC-OVR-002. Запрос консультации у инвест-консультанта ПИН (+2a), UC-OVR-004. От реферальной ссылки до объекта у собственника, UC-OVR-011. Самозапись инвестора: от слота до исхода встречи и лида в воронке
- registries (1): Реестр событий PIN
Связанные документы
- Сущности: lead.md (дом полей, INV-CRM-001,
LeadSource), client.md (ClientId). - Upstream-контракт (порождает
Source = Referral): referral lead-attach-requested; журнал привязки — referral-registration.md. - Соседний топик контекста: lead-expired.md; карта событий/консюмеров — README контекста.
- ADR: 0052 tenancy, 0056 simplicity-first.