Event Contract: pin.chessboard.developer-upserted (DeveloperUpsertedKafkaEvent)
Версионируемый контракт интеграционного события «застройщик создан/обновлён». Consumer-документы ссылаются
сюда, не переописывая payload. Это «developer-upserted»-событие шахматки: публичный snapshot профиля
девелопера — верх иерархии Developer → Project → Building → PropertyObject. У агрегата нет отдельного
status-события: смена публичности (Draft/Published/Hidden) едет тем же upsert-ом (в отличие от объекта
недвижимости — property-object-upserted), так как поток изменений
застройщиков низкочастотный.
Назначение
Фиксирует создание DeveloperEntity (consumer компаний identity при Type = Developer, синк Talent
Chessboard, admin-операция) и каждое значимое обновление публичных полей карточки
(Name / Status / LogoFileId / Highlights). Событие публичное: даёт downstream-контекстам snapshot
застройщика без синхронного запроса в chessboard — карточка девелопера витрины, верхнее измерение
статистики (ADR-0057), отображение застройщика в сделках CRM, контекст правил вознаграждений.
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.chessboard.developer-upserted |
| C# type | DeveloperUpsertedKafkaEvent |
| Topic | pin.chessboard.developer-upserted.v1 |
| Partitions / key | 3 партиции (застройщиков сотни — низкая кардинальность) / key = Id — порядок per-developer гарантирован |
| ContractVersion | 1 |
| Producer | chessboard: Developers API управления профилем, consumer компаний identity-companies (pin.identity.companies.v1, Type = Developer), синк talent-chessboard-sync |
| AggregateType | Developer (агрегат DeveloperEntity) |
| Статус | draft |
Событие несёт метаданные: идентификатор сообщения (для дедупликации), время, идентификатор и тип
агрегата-источника (AggregateId = Id, AggregateType = "Developer"); сквозной trace восстанавливается из
метаданных. Публикуется атомарно со сменой состояния агрегата (транзакционный outbox).
Payload DeveloperUpsertedKafkaEvent
Публичный snapshot карточки застройщика. Дом полей — entity: DeveloperEntity;
здесь перечислено только то, что реально в событии.
| Поле | Тип | Обязательность | Источник (поле агрегата) | Compatibility | Описание |
|---|---|---|---|---|---|
Id | Guid | Да | DeveloperEntity.Id | Immutable | Ключ агрегата (= Kafka key). Он же CompanyId в identity и TenantId дочерних сущностей шахматки (ADR-0052). |
Status | string (enum DeveloperStatus) | Да | DeveloperEntity.Status | Additive only (новые значения enum) | Draft / Published / Hidden — публичность профиля. Hidden каскадно скрывает карточку и все проекты (INV-DEV-6). |
Name | string | Да | DeveloperEntity.Name | Additive only | Публичное название застройщика (≤ 256, min 1). Реплика-поле при SyncSource = TalentChessboard. |
LegalName | string? | Нет | DeveloperEntity.LegalName | Additive only | Юридическое наименование (карточка/декларации; ≤ 512). |
LogoFileId | Guid? | Нет | DeveloperEntity.LogoFileId | Additive only | Файл логотипа во внешнем storage; выдача ссылок — граница storage/CDN. |
FoundedYear | short? | Нет | DeveloperEntity.FoundedYear | Additive only | Год основания (1900..2100) — блок «опыт застройщика». |
Highlights | DeveloperHighlightsDocument | Да | DeveloperEntity.Highlights (typed jsonb) | Additive only | Показатели публичной карточки (см. вложенную модель ниже). |
Version | long | Да | DeveloperEntity.Version | Immutable semantics | Доменная версия агрегата; монотонный guard у потребителей (событие с меньшей/равной версией — skip). Version = 1 ⇔ создание профиля. |
Вложенная модель DeveloperHighlightsDocument
Typed-jsonb, дом — developer.md, раздел «Модели JSONB» (ContractVersion = 1);
здесь — состав, реально едущий в событии.
| Поле | Тип | Обязательность | Ограничения | Описание |
|---|---|---|---|---|
ContractVersion | int | Да | = 1 | Версия схемы документа (независима от ContractVersion события). |
ObjectsDeliveredCount | int? | Нет | >= 0 | Сдано объектов. |
ObjectsInProgressCount | int? | Нет | >= 0 | Строится объектов. |
YearsOnMarket | short? | Нет | 0..200; null — фронт вычисляет из FoundedYear | Лет на рынке. |
ReliabilityValue | decimal? | Нет | 0..ReliabilityMax, precision (4,1) | Показатель надёжности. |
ReliabilityMax | decimal | Да | default 10, precision (4,1) | Верхняя граница шкалы надёжности. |
AwardTitles | IReadOnlyList<string> | Да (может быть пустым) | элемент ≤ 256, ≤ 20 элементов | Награды/титулы. |
Служебные и локально-денормализованные поля агрегата (SyncSource, ExternalTalentDeveloperId,
ReplicaVersion, LastSyncedAt, ProjectsPublishedCount, audit) в payload не входят — они внутренние
для owning-контекста. Description, Website, ContactPhone, ContactEmail в minimal-snapshot v1 не
включены (организационные, не PII); при потребности витрины добавляются additive без bump.
Когда публикуется
| Условие | Переход состояния | Согласованность |
|---|---|---|
Создание профиля: consumer компаний identity (Type = Developer), синк Talent Chessboard или admin-операция | [*] → Draft | публикация атомарна со сменой состояния агрегата |
Значимое обновление Status: Publish / Hide / Unhide / Unpublish (Developers API), suspend компании в identity (Published → Hidden) | переход по stateDiagram developer.md | та же |
Значимое обновление Name / LogoFileId (синк Talent — реплика-поля; Developers API — при SyncSource = Pin) или Highlights (Developers API — PIN-поле) | без перехода | та же |
Не публикуется при изменении только LegalName / FoundedYear / Description / Website / контактов — низкоценные/служебные поля; свежее значение LegalName/FoundedYear переносится в snapshot при следующем значимом изменении или создании | — | — |
Переход Status дополнительно поднимает доменное DeveloperStatusChangedEvent для локальных обработчиков;
наружу идёт только этот upsert (отдельного status-топика нет).
Потребители (consumers)
| Consumer-контекст | Эффект (событие → что меняется) | Идемпотентность | Задержка (SLA) |
|---|---|---|---|
catalog | Проекция карточки застройщика витрины (название, лого, Highlights); Hidden убирает карточку и проекты из public-проекций | MessageId + guard по Version | < 1 мин |
dev-stats | Реплика верхнего измерения Developer иерархии Developer→Project→Building→LayoutGroup→Layout→PropertyObject (ADR-0057) | то же | < 5 мин |
crm | Отображение застройщика в сделках/карточках объектов (название, лого) | то же | < 5 мин |
rewards | Контекст застройщика для правил вознаграждений (привязка программ к девелоперу) | то же | < 5 мин |
Доставка и обработка отказов
- Доставка — at-least-once. Транзакционный outbox гарантирует, что событие не теряется при коммите
агрегата, но допускает повторную доставку; порядок сообщений по ключу (
Id) сохраняется в пределах партиции. - Идемпотентность потребителя. Дубликат отсеивается по идентификатору сообщения; переупорядоченное или
устаревшее событие отбрасывается монотонным
Version-guard-ом (событие с версией ≤ применённой — skip), поэтому повторная доставка не меняет реплику. - Ретраи. Временная ошибка потребителя (недоступность его БД/зависимости) → повтор с backoff до успеха; событие не теряется, порядок per-developer не нарушается.
- DLQ. Стабильно необрабатываемое сообщение («ядовитое») после исчерпания ретраев уходит в DLQ
потребителя с алертом — чтобы не блокировать партицию; расхождение реплики закрывается повторной обработкой
из DLQ либо ресинком из источника (
chessboard). - Окно согласованности. Пока потребитель не догнал (SLA из таблицы «Потребители»), downstream показывает
прежний профиль застройщика; каскад
Hidden(скрытие карточки и проектов) виден потребителям в пределах их SLA — до этого проекты застройщика ещё отдаются публичными проекциями.
Версионирование и совместимость
- Версия — один
ContractVersion = 1; при несовпадении версии события — warn-and-proceed. Топик кодирует версию конвенцией суффикса.v1. - Additive-only для minor: новые поля — optional с безопасным default, без bump (тот же принцип для
DeveloperHighlightsDocument— новое поле nullable/default той жеContractVersion). - Breaking (удаление/переименование/смена типа/смысла поля) — новый
ContractVersion = 2+ новый топикpin.chessboard.developer-upserted.v2+ dual-publish на период миграции + deadline миграции consumers + sunset.v1. - Unknown enum
Statusу потребителя — fallback: warning + метрикаchessboard_sync_unknown_enum_total, сохранение строки как есть; поведение — как для наиболее ограничивающего значенияHidden(не показывать, fail closed). - Deprecated-версия — sunset через 2 релиза после миграции последнего потребителя.
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
Id, Version | Internal | include |
Status, Name, LegalName, FoundedYear, Highlights.* | Public (данные о застройщике публичны — карточка каталога, юрданные по 214-ФЗ) | include |
LogoFileId | Internal (ссылка на файл; сам файл — storage/CDN, не в событии) | include |
PII в событии нет: застройщик — организация, контакты организационные и в payload не включены, поэтому
маскирование не требуется. Событие НЕ содержит secrets, tokens, raw credentials, персональных/платёжных
реквизитов, unbounded вложений (лого — только FileId). Если организационные контакты когда-либо добавят
additive — они остаются классом Public (не PII конкретного лица) и не требуют маскирования.
Обратные ссылки
Автоссылки: где используется этот документ.
- События (1): Event Contract: pin.chessboard.project-upserted (ProjectUpsertedKafkaEvent)
- Консюмеры (4): Consumer: Chessboard Projection Sync (pin-catalog-chessboard-sync), Consumer: DevStatsDeveloperUpsertedConsumer, Consumer: Identity Companies (pin-chessboard-identity-companies), Consumer: Talent Chessboard Sync (pin-chessboard-talent-sync)
- Фоновые задачи (1): Background Job: talent-initial-import (первичная загрузка / bootstrap из Talent Chessboard)
- dimensions (1): dim_developer (ClickHouse dimension)
- registries (1): Реестр событий PIN
Связанные документы
- Источники: consumer компаний identity-companies
(топик
pin.identity.companies.v1,Type = Developer→ создание профиля; suspend →Hidden), синк talent-chessboard-sync, Developers API управления профилем. - Сущность и инварианты: developer.md; enum
DeveloperStatus— там же;ChessboardSyncSource— reference-data.md. - Соседние события агрегатов иерархии: project-upserted.md, property-object-upserted.md.
- ADR-0052 (tenancy), ADR-0057 (dev-stats).