dim_building (ClickHouse dimension)
Реплика-измерение «Здание» (литер / корпус) в БД dev-stats. JOIN-free справочник для резолва
имени/статуса здания и для фильтрации метрик по уровню Building (опциональный уровень иерархии, ADR-0057).
Дом полей источника — chessboard: BuildingEntity; здесь — только
колонки, нужные dev-stats.
Назначение
dim_building хранит текущий снапшот здания (одна логическая строка на BuildingId), реплицируемый из
pin.chessboard.building-upserted.v1. Даёт подпись графика
(Name — «Литер 3») и родительские id для восхождения по иерархии при обогащении фактов и фильтрации.
Не факт, а стабильное измерение (ReplacingMergeTree).
Table
| Поле | Значение |
|---|---|
| Database | pin_dev_stats |
| Table | dim_building |
| Engine | ReplacingMergeTree(UpsertedAt) |
| Owner service | dev-stats (DevStats.Background пишет, DevStats.Api читает; DDL — DevStats.Migrator) |
| Source events | pin.chessboard.building-upserted.v1 |
| Partition / Order | без партиционирования (десятки тысяч строк); ORDER BY (BuildingId) |
| Freshness SLA | ≤ 5 минут от события до видимости |
Стабильное измерение, не append-only факт: последняя версия перезаписывает предыдущую. Онлайн-чтение —
дубль-устойчивое, без FINAL: актуальная строка берётся argMax(col, UpsertedAt) c GROUP BY BuildingId.
Columns
| Column | Type | Nullable | Source (поле BuildingEntity) | Partition/Order | PII |
|---|---|---|---|---|---|
BuildingId | UUID | нет | Id | order (единственный ключ) | нет |
ProjectId | UUID | нет | ProjectId — родитель | none | нет |
DeveloperId | UUID | нет | TenantId (= DeveloperId проекта) — вверх по иерархии | none | нет |
Name | String | нет | Name (литер/корпус, «Литер 3») | none | нет |
Status | LowCardinality(String) | нет | Status — Draft / Published / Hidden | none | нет |
ConstructionStatus | LowCardinality(String) | нет | ConstructionStatus — Planned / UnderConstruction / Commissioned / Delivered | none | нет |
FloorsTotal | Int16 | нет | FloorsTotal (этажность — для подписи корпуса) | none | нет |
SourceVersion | Int64 | нет | Version источника — монотонный guard | none | нет |
UpsertedAt | DateTime64(3) | нет | время события (CreatedAt) | none (версия движка) | нет |
Минимальный набор для фильтрации/подписи. Сроки сдачи, ReadinessPercent, счётчики объектов недвижимости в dev-stats
не нужны — не реплицируются. DeveloperId денормализуется из TenantId события, чтобы восхождение по
иерархии и проверка scope шли без обращения к dim_project.
Инварианты
- INV-DIM-BLD-1 (единственность): одна логическая строка на
BuildingId(единственный компонентORDER BY); merge оставляет строку с максимальнымUpsertedAt. - INV-DIM-BLD-2 (родители денормализованы):
ProjectIdиDeveloperIdреплицируются в строку здания, чтобы восхождение по иерархии и проверка scope шли без обращения кdim_project. - INV-DIM-BLD-3 (не удаляется): строка живёт, пока существует здание;
Status = Hiddenпомечает скрытость, но не удаляет (3-летние факты должны резолвить подпись/фильтр по уровнюBuilding). - INV-DIM-BLD-4 (precedence): актуальность — по
UpsertedAt(max побеждает);SourceVersionмонотонна per-key, служит анти-out-of-order-диагностикой. - INV-DIM-BLD-5 (минимальность): реплицируется только набор для подписи/фильтра (
Name/Status/ConstructionStatus/FloorsTotal); сроки/готовность/счётчики не хранятся; PII нет.
Кросс-сервисный контракт
- Ожидает от
chessboard(building-upserted):Id,ProjectId,TenantId(=DeveloperId),Name,Status,ConstructionStatus,FloorsTotal,Version;key = BuildingId, at-least-once, атомарная публикация снапшота, additive-only для minor. - Предоставляет downstream (в пределах
dev-stats): JOIN-free подпись (Name— «Литер 3») и атрибуты для фильтра метрик по опциональному уровнюBuilding. Измерение не на критическом пути обогащения факта: факт несёт денормализованныйBuildingId(изdim_property_object);dim_buildingнужен для подписи/фильтра при чтении. - При отсутствии/устаревании: строки ещё нет → метрика уровня
Buildingрезолвится поBuildingId, подпись деградирует до id без ошибки; устаревшая доставка → берётся max(UpsertedAt).
Deduplication
- Dedup key:
BuildingId;ReplacingMergeTree(UpsertedAt)оставляет строку с максимальнымUpsertedAt. - Replay behavior: идемпотентный upsert; до merge — дубль-устойчивое чтение
argMax(…, UpsertedAt) … GROUP BY BuildingId(FINALв online запрещён). - Version guard: событие применяется только при
event.Version > SourceVersion; иначе пропуск.
Partition И TTL
- Partition key: нет — зданий десятки тысяч максимум.
- Order key:
(BuildingId)— точечный резолв и фильтрWHERE DeveloperId = актор AND ProjectId = … AND BuildingId = …. - TTL/retention: нет — измерение живёт, пока существует здание;
Status = Hiddenпомечает скрытость, строку не удаляет (3-летние факты должны резолвиться).
Consumers И Backfill
| Process | Назначение | Checkpoint |
|---|---|---|
Приём pin.chessboard.building-upserted.v1 (фоновый ingest) | Проверка версии, маппинг snapshot → строка измерения, batch-upsert (≤ 1000 / 5 с) | Kafka consumer offset (commit после вставки батча) |
| Backfill измерений | Повтор топика building-upserted при вводе сервиса (идемпотентно) | Kafka offset / диапазон BuildingId |
Security
- Raw PII не хранится (здание — объект, не пользователь).
- Доступ — роль застройщика, permission
dev-stats.view, scope строго = свойDeveloperId; каждый запрос фильтруетDeveloperId = актор. - ClickHouse не operational source of truth: источник здания —
chessboard.
Acceptance criteria
- Happy upsert. Given
building-upsertedс непустымId, When обработан батч, Then вdim_buildingесть строка сName/Status/ConstructionStatus/FloorsTotalи родителямиProjectId/DeveloperId; чтениеargMax(…, UpsertedAt) GROUP BY BuildingId(безFINAL) отдаёт значения. - Идемпотентный повтор / устаревшая доставка. Given то же/старое событие доставлено повторно, When обработано,
Then после merge логически одна строка; побеждает max(
UpsertedAt). - Hidden не удаляет. Given здание переведено в
Status = Hidden, When событие обработано, Then строка остаётся, факты 3-летней глубины резолвят подпись. - Деградация подписи. Given строки ещё нет при чтении метрики уровня
Building, Then метрика резолвится поBuildingId, подпись показывает id без ошибки. - Scope. Given запрос застройщика, When фильтруется
dim_building, Then только строкиDeveloperId = актор.
Обратные ссылки
Автоссылки: где используется этот документ.
- Консюмеры (1): Consumer: DevStatsBuildingUpsertedConsumer
- dimensions (5): dim_developer (ClickHouse dimension), dim_layout (ClickHouse dimension), dim_layout_group (ClickHouse dimension), dim_project (ClickHouse dimension), dim_property_object (ClickHouse dimension)
- registries (1): Реестр сущностей PIN
Связанные документы
- Источник: chessboard:
BuildingEntity, событие building-upserted. - Соседние измерения: developer.md, project.md, layout-group.md, layout.md, property-object.md.
- Шаблон: clickhouse-fact-template.md; ADR-0057, ADR-0056.