Event Contract: pin.invest-analytics.property-object-metrics-updated (PropertyObjectMetricsUpdatedKafkaEvent)
Версионируемый контракт интеграционного события «метрики доходности объекта недвижимости пересчитаны».
Producer — invest-analytics; событие несёт полный snapshot витринных метрик доходности одного объекта
недвижимости (окупаемость, надёжность, прогнозный рост, арендный доход, рейтинг) на момент расчёта AsOf.
Имя типа события зафиксировано ADR-0058.
Назначение
Фиксирует пересчёт метрик доходности объекта недвижимости, чтобы витрина (catalog) держала их
денормализованно и не ходила синхронно в invest-analytics на каждую карточку/ленту (ADR-0056 «одна строка
без джойнов»). Публикация закрывает расхождение DSC-CAT-4 каталога
(invest-analytics-enrichment.md).
Metadata
| Поле | Значение |
|---|---|
| Event type | pin.invest-analytics.property-object-metrics-updated |
| Тип события | PropertyObjectMetricsUpdatedKafkaEvent |
| Topic | pin.invest-analytics.property-object-metrics.v1 |
| Partitions / key | 3 (объём — число объектов недвижимости × частота пересчёта, ADR-0056) / key = PropertyObjectId — порядок snapshot-ов по объекту недвижимости гарантирован |
| ContractVersion | 1 |
| Producer | invest-analytics: пересчёт метрик по объекту недвижимости — см. «Когда публикуется» |
| Статус | draft |
Событие публикуется через транзакционный outbox — атомарно с фиксацией результата пересчёта.
Идентификатор и тип агрегата-источника — PropertyObjectId / PropertyObjectMetrics; ключ дедупликации
сообщения используется потребителем.
Payload PropertyObjectMetricsUpdatedKafkaEvent
Полный snapshot метрик на момент AsOf: null в поле означает «метрика недоступна/отозвана» и
очищает сырое значение у потребителя (эффективное может удержать оверрайд каталога). Метрики — производные
серверного расчёта серий доходности (дом формул — investor-series-models.md,
AsideMetrics). Целевые поля витрины — catalog-property-object-card.md
(Payback, Reliability, GrowthPercent, RentalIncomePerYear, Rating).
| Поле | Тип | Обязательность | Источник (расчёт) | Compatibility | Описание |
|---|---|---|---|---|---|
PropertyObjectId | Guid | Да | параметры объекта недвижимости (chessboard) | Immutable | Ключ агрегата (= ключ сообщения). |
ProjectId | Guid | Да | измерения объекта недвижимости (снапшот события цены / chessboard) | Immutable | Проект объекта — адресация проектной части витрины и сверка целостности проекций. |
PaybackValue | decimal? | Нет | окупаемость из ROI (AsideMetrics.RoiPerYear) | Additive only | Значение окупаемости; пара с PaybackPeriod (оба заданы или оба null). |
PaybackPeriod | string? (enum PaybackPeriod: Years/Months) | Нет | единица окупаемости | Additive only (новые значения) | Единица измерения окупаемости. |
GrowthPercent | decimal? | Нет | прогнозный рост цены (AsideMetrics.Growth12mPercent) | Additive only | Прогнозный рост цены, %/год (диапазон витрины -99.99..999.99). |
RentalIncomePerYear | decimal? | Нет | арендный доход за год расчёта | Additive only | Прогнозный арендный доход, руб/год (>= 0). |
Rating | decimal? | Нет | модельный композитный рейтинг | Additive only | Рейтинг объекта, 0.0..10.0. |
ReliabilityValue | short? | Нет | модельный индекс надёжности | Additive only | Значение индекса надёжности; пара с ReliabilityMax. |
ReliabilityMax | short? | Нет | шкала индекса надёжности | Additive only | Максимум шкалы (1..100). |
ProjectGrowthPercent | decimal? | Нет | агрегат по объектам проекта | Additive only | Метрика уровня проекта; null = проектная часть не пересчитывалась (поле проекта не трогается). |
ProjectRating | decimal? | Нет | агрегат по объектам проекта | Additive only | Рейтинг уровня проекта; та же семантика null. |
AsOf | DateTimeOffset | Да | момент расчёта (AnchorTime/now) | Immutable | Бизнес-время пересчёта — монотонный guard потребителя (применяется только при AsOf > SourceSnapshot.AnalyticsAsOf). |
Version | long | Нет | ревизия пересчёта | Additive only | Дополнительный монотонный guard пересчёта (по умолчанию потребитель каталога упорядочивает по AsOf). |
Вложенных моделей нет — payload плоский (Payback*/Reliability* — плоские пары, как в дому проекции
витрины). Единственный per-object snapshot; «обязательность» относится к одному сообщению.
Когда публикуется
Пересчёт метрик по объекту недвижимости выполняет отдельный recompute-компонент invest-analytics
(движок серий → компактная проекция витринных метрик) — не raw-ingestion consumer цен (тот пишет только
append-only факты в ClickHouse без outbox). Публикуется всегда как полный snapshot результата, атомарно
с фиксацией пересчёта (PG-checkpoint + outbox).
| Условие (триггер) | Что пересчитывается | Согласованность |
|---|---|---|
Изменение цены — recompute-компонент реагирует на pin.chessboard.property-object-price-changed.v1 (после append факта в ClickHouse) | метрики затронутого объекта недвижимости | пересчёт + outbox в одном согласованном изменении |
Consumed pin.invest-analytics.model-changed.v1 (правка эконом-модели — internal recompute job, model-changed.md) | метрики объектов недвижимости, затронутых scope-ом (Global — все / PropertyObjectType — по GroupId) | та же |
Плановый пересчёт (freshness — метрики зависят от текущего месяца AnchorTime) | метрики объектов недвижимости батчем | та же |
Не публикуется: пересчёт дал те же значения при том же AsOf (естественная идемпотентность) | — | — |
Потребители (consumers)
| Consumer-контекст | Consumer | Side effects | Идемпотентность | Задержка (SLA) |
|---|---|---|---|---|
catalog | InvestAnalyticsPropertyObjectMetricsConsumer | Обновление денормализованных Payback/Reliability/GrowthPercent/RentalIncomePerYear/Rating карточки объекта недвижимости (сырые → SourceSnapshot, эффективные Override ?? Source) + проектных GrowthPercent/Rating (invest-analytics-enrichment.md) | ключ сообщения + монотонный guard по AsOf | < 15 мин (SLO метрик витрины) |
Отказы и восстановление
| Сценарий | Поведение |
|---|---|
| Сбой consumer каталога на сообщении | retry с backoff; offset не двигается — snapshot применится после восстановления (at-least-once) |
| Повторная доставка / переигрывание топика | монотонный guard по AsOf: применяется только AsOf > SourceSnapshot.AnalyticsAsOf; более старый/равный snapshot — skip |
| Исчерпание retry | сообщение в DLQ + алерт; карточка сохраняет прежние (сырые) метрики — деградация «устаревший бейдж», не обнуление |
| Producer переиздал полный набор (rebuild проекций каталога, включение producer-а) | безопасно: полное наполнение перезапишет сырые значения; оверрайды каталога сохранятся (эффективное = Override ?? Source) |
null в поле метрики | очищает сырое значение у потребителя; эффективное может удержать оверрайд каталога (Override ?? Source) |
Порядок snapshot-ов одного объекта недвижимости гарантирован ключом партиционирования PropertyObjectId;
между разными объектами порядок не важен — каждая карточка обновляется независимо.
Версионирование и совместимость
- Версия —
ContractVersion = 1; при несовпадении версии сообщения consumer логирует warning и продолжает (без upcasting/schema-migration). Топик кодирует версию конвенцией суффикса.v1. - Additive-only для minor: новые поля — optional с безопасным default, без bump
ContractVersion. - Breaking (удаление/переименование/смена типа/смысла) —
ContractVersion = 2+ новый топикpin.invest-analytics.property-object-metrics.v2+ dual-publish на период миграции + deadline миграции consumers + sunset.v1. - Unknown enum (
PaybackPeriod) у потребителя — fallback: параPayback*отбрасывается + warning (правило каталога), событие не валится. - Snapshot-семантика делает re-read топика безопасным (полное наполнение метрик после включения producer-а, rebuild проекций каталога).
Безопасность
| Поле | Класс данных | Правило |
|---|---|---|
PropertyObjectId, ProjectId, AsOf, Version | Internal | include |
PaybackValue, PaybackPeriod, ReliabilityValue, ReliabilityMax, GrowthPercent, RentalIncomePerYear, Rating, ProjectGrowthPercent, ProjectRating | Public (витринные display-метрики; наружу отдаются в карточках каталога) | include |
PII в событии отсутствует: событие описывает объект недвижимости, а не пользователя (расчёт детерминирован по параметрам объекта и платформенной эконом-модели; субъект/налоговый режим в витринные метрики не входят). Событие НЕ содержит эконом-модель (Internal — наружу уходят только вычисленные метрики), secrets, tokens, credentials, платёжных реквизитов, unbounded-вложений.
Обратные ссылки
Автоссылки: где используется этот документ.
- События (2): События invest-analytics: карта контрактов, Event Contract: pin.invest-analytics.model-changed (EconomicModelChangedKafkaEvent)
- Консюмеры (2): Consumer: ChessboardPropertyObjectPriceChangedConsumer, Consumer: Invest-Analytics Enrichment (pin-catalog-analytics-enrichment)
- index.md (1): InvestAnalytics Service
- registries (1): Реестр событий PIN
Связанные документы
- Расчёт метрик: investor-series-models.md (
AsideMetrics), economic-model-config.md (коэффициенты), price-history-fact.md (историческая часть). - Триггер правки модели: model-changed.md.
- Потребитель: invest-analytics-enrichment.md; целевая проекция — catalog-property-object-card.md.
- Карта событий контекста: README.md; обзор — ../README.md.
- ADR-0056, ADR-0058.