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

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 typepin.invest-analytics.property-object-metrics-updated
Тип событияPropertyObjectMetricsUpdatedKafkaEvent
Topicpin.invest-analytics.property-object-metrics.v1
Partitions / key3 (объём — число объектов недвижимости × частота пересчёта, ADR-0056) / key = PropertyObjectId — порядок snapshot-ов по объекту недвижимости гарантирован
ContractVersion1
Producerinvest-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Описание
PropertyObjectIdGuidДапараметры объекта недвижимости (chessboard)ImmutableКлюч агрегата (= ключ сообщения).
ProjectIdGuidДаизмерения объекта недвижимости (снапшот события цены / chessboard)ImmutableПроект объекта — адресация проектной части витрины и сверка целостности проекций.
PaybackValuedecimal?Нетокупаемость из ROI (AsideMetrics.RoiPerYear)Additive onlyЗначение окупаемости; пара с PaybackPeriod (оба заданы или оба null).
PaybackPeriodstring? (enum PaybackPeriod: Years/Months)Нетединица окупаемостиAdditive only (новые значения)Единица измерения окупаемости.
GrowthPercentdecimal?Нетпрогнозный рост цены (AsideMetrics.Growth12mPercent)Additive onlyПрогнозный рост цены, %/год (диапазон витрины -99.99..999.99).
RentalIncomePerYeardecimal?Нетарендный доход за год расчётаAdditive onlyПрогнозный арендный доход, руб/год (>= 0).
Ratingdecimal?Нетмодельный композитный рейтингAdditive onlyРейтинг объекта, 0.0..10.0.
ReliabilityValueshort?Нетмодельный индекс надёжностиAdditive onlyЗначение индекса надёжности; пара с ReliabilityMax.
ReliabilityMaxshort?Нетшкала индекса надёжностиAdditive onlyМаксимум шкалы (1..100).
ProjectGrowthPercentdecimal?Нетагрегат по объектам проектаAdditive onlyМетрика уровня проекта; null = проектная часть не пересчитывалась (поле проекта не трогается).
ProjectRatingdecimal?Нетагрегат по объектам проектаAdditive onlyРейтинг уровня проекта; та же семантика null.
AsOfDateTimeOffsetДамомент расчёта (AnchorTime/now)ImmutableБизнес-время пересчёта — монотонный guard потребителя (применяется только при AsOf > SourceSnapshot.AnalyticsAsOf).
VersionlongНетревизия пересчёта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-контекстConsumerSide effectsИдемпотентностьЗадержка (SLA)
catalogInvestAnalyticsPropertyObjectMetricsConsumerОбновление денормализованных 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, VersionInternalinclude
PaybackValue, PaybackPeriod, ReliabilityValue, ReliabilityMax, GrowthPercent, RentalIncomePerYear, Rating, ProjectGrowthPercent, ProjectRatingPublic (витринные display-метрики; наружу отдаются в карточках каталога)include

PII в событии отсутствует: событие описывает объект недвижимости, а не пользователя (расчёт детерминирован по параметрам объекта и платформенной эконом-модели; субъект/налоговый режим в витринные метрики не входят). Событие НЕ содержит эконом-модель (Internal — наружу уходят только вычисленные метрики), secrets, tokens, credentials, платёжных реквизитов, unbounded-вложений.

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

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

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