diff --git a/docs/architecture/dds/README.md b/docs/architecture/dds/README.md index 6308b32..b447f43 100644 --- a/docs/architecture/dds/README.md +++ b/docs/architecture/dds/README.md @@ -54,10 +54,6 @@ - Наполняет слой [дневной конвейер](../etl.md): запуск, объём прогона и замена партиций на кластере — его устройство. -## Открытые решения - -- Витрины DM — #135. - ## Родословная Дом набора и первая сущность собраны развилкой #130 карты #127; форма diff --git a/docs/architecture/dm.md b/docs/architecture/dm.md new file mode 100644 index 0000000..5338702 --- /dev/null +++ b/docs/architecture/dm.md @@ -0,0 +1,121 @@ +# Слой DM: витрины + +Как модель DDS превращается в витрины для потребителя BI: какую форму +принимает витрина, кто какие витрины строит и как слой проверяется. Состав +колонок каждой витрины задаёт раздел 7 мастер-спеки; здесь — форма, +наполнение и контроль. + +## Форма: по зерну, не по цене запроса + +Витрина, меняющая зерно источника — дневной агрегат, сверка по заказам, — +материализуется: `dm.<имя>_rep` / `_dist` и публичное `dm.<имя>_v` с явным +списком колонок, по общим [конвенциям хранилища](storage.md). Наполняет её +`dm_transform` заменой дневных партиций дышащего окна K — той же механикой, +что у сущностей DDS ([конвейер](etl.md)). Витрина, сохраняющая зерно +источника — переименование, обогащение строки, — остаётся обычным +представлением: копию событий ради других имён колонок не держит ни один +боевой склад. + +Правило выбрано учебной целью, а не ценой запроса. На объёмах стенда +представлениями обошлось бы всё: условия сквозь цепочку представлений +проходят ([исследование](../research/2026-08-22-view-predicate-pushdown.md)). +Но агрегат, пересобираемый партициями, — производственный паттерн, ради +которого стенд и строится, а пара «таблица рядом с представлением» +показывает: форма — решение по роли витрины, у одного слоя законны обе. + +## Кто строит: стенд и менти + +Стенд привозит готовыми витрины трёх сюжетов дашборда этапа 8 и +инфраструктуру контроля: + +- `revenue_daily` — выручка по дням и категориям; образец материализованной + витрины, на него ссылаются учебные задания; +- `purchase_vs_orders` — сверка A+C с классами расхождений; зерно — заказ, + шардирование сохраняет ко-локацию заказов по `cityHash64(order_id)`; +- `daily_traffic` — пара «посетители» / «известные пользователи», локальное + обогащение картой идентичностей; +- `dq_summary` — сводка проверок качества (ниже). + +Остальные витрины спеки — учебные задания: менти строит их сам по ТЗ с +образцом. Разминка — представление `events_enriched_v` (зерно события), +затем агрегаты `top_pages_daily`, `session_overview` и `dq_errors_daily`, +последней — самая содержательная `utm_effectiveness` с уроком `declared_*`. + +Дашборд этапа 8 стоит только на витринах стенда: приёмка этапа не зависит +от выполненных домашек. Расширение дашборда визуализациями по своим +витринам — отдельная домашка после этапа 8. + +## `dm.dq_summary` + +Зерно — «день данных × проверка»: строка отвечает, каково качество дня +сейчас, а не сколько раз его проверяли, — история прогонов и так живёт в +Airflow. Партиция — день данных; прогон звена DQ заменяет партиции +проверенных дней через `REPLACE PARTITION`. Обещанный спекой перевод с +`TRUNCATE+INSERT` выполняется той же механикой замены, что и у витрин, — +ничего отдельного учить не надо. + +## Звено DQ конвейера + +Встаёт в `etl_pipeline` после DM ([конвейер](etl.md)) и проверяет только +объекты стенда: + +- счётная сверка сессий с эталоном `VisitID` — кука × день, эталон читается + из `ods.event_v` ([модель сессии](dds/session.md)); +- счётчики классов расхождений против описи мира — по дням с закрытым + окном; +- счётные сверки витрин стенда с их DDS-источниками. + +Результаты ложатся в `dm.dq_summary`. Звено — не только сторож конвейера, +но и экспонат: менти видит, как контроль качества устроен в бою — по +расписанию, с историей, рядом с трансформациями. + +## Даг самопроверки + +Домашки контролирует отдельный даг без расписания: менти запускает его +рукой. Задача витрины красная, пока витрины нет или счёт не сходится, и +зелёная, когда построено верно; сообщение об ошибке говорит, что не так и +куда смотреть. Проверки — инварианты против DDS-источников, золотые +константы не прибиваются. В `dq_summary` даг не пишет: он инструмент лабы, +а не история качества конвейера. + +Приём проверен на greenplum-стенде предшественника: валидационный даг с +группами проверок по слоям давал менти обратную связь по каждому шагу +домашки, не трогая рабочий конвейер. + +## Остаётся нарезке + +- Имена задач `dm_transform`, звена DQ и дага самопроверки. +- Ключи шардирования материализованных витрин — вместе с их DDL; правило + партиции-донора — в [доке хранилища](storage.md). +- Тексты учебных заданий и точный список проверок обоих контуров. +- Механика сверки A+C в SQL — рамка в спеке, разделы 4 и 7. + +## Отклонённые варианты + +- **Весь слой представлениями.** Дёшево и всегда свежо, но главный + производственный паттерн слоя — пересборка агрегатов — остался бы + невидим, а `dm_transform` — без работы. +- **Материализовать всё.** Копия событий ради переименования колонок — + антипаттерн, который менти увёз бы в бой. +- **Гибрид по цене запроса.** Выбор формы по стоимости, которой на объёмах + стенда нет: сторож без настоящей опасности. +- **Проверки домашек в конвейерном звене DQ.** Конвейер стоял бы красным до + выполнения лаб, а проверка, которая всегда красная, приучает себя + игнорировать. +- **«Жёлтый» статус недостроенной витрины.** У задачи Airflow нет такого + состояния; изображать его логами — семантика, которую пришлось бы + расшифровывать. Красное «ещё не построено» честнее. +- **Самопроверка пишет в `dq_summary`.** Попытки менти смешались бы с + историей качества конвейера. +- **Superset поверх DDS без слоя DM.** Терялся бы урок «витрина — контракт + под потребителя», против раздела 7 спеки. + +## Связанные решения + +- Мастер-спека, разделы 4 и 7, — состав колонок витрин и классы сверки. +- [Дневной конвейер](etl.md) — запуск `dm_transform`, объём прогона, + замена партиций на кластере. +- [Дока хранилища](storage.md) — имена объектов, граница доступа через + `_v`, партиция-донор. +- [Карта модели DDS](dds/README.md) — источники витрин. +- Резолюция — тикет #135 карты #127. diff --git a/docs/architecture/etl.md b/docs/architecture/etl.md index 41a4d42..cd8adea 100644 --- a/docs/architecture/etl.md +++ b/docs/architecture/etl.md @@ -14,8 +14,8 @@ Дневной конвейер на стенде один — даг `etl_pipeline`. Своей работы у него нет: он дёргает слойные даги последовательно и ждёт каждый — -`dds_transform`, затем `dm_transform`. Контур DQ этапа 4 встанет его же -звеном после DM; состав звена — предмет развилки витрин (#135) и нарезки. +`dds_transform`, затем `dm_transform`. Контур DQ этапа 4 встаёт его же +звеном после DM; состав звена — в [доке слоя DM](dm.md). Слойные даги устроены по образцу `orders_ingest`: без расписания, один активный прогон, запускаются триггером конвейера или рукой при разборе @@ -42,7 +42,8 @@ Параметров у конвейера нет: объём он выводит сам из `world_position` и состояния слоёв. Сессии догоняют хвост — от последнего собранного дня до последнего прожитого; заказы пересобирают дышащее окно K от позиции; карта -идентичностей пересчитывается целиком; правила витрин — у витрин (#135). +идентичностей пересчитывается целиком; правила витрин — у +[слоя DM](dm.md). Поэтому один и тот же запуск одинаково верен из триггера, с кнопки менти и после падения, а разгону на семь дней хватает одного прогона. @@ -70,7 +71,8 @@ диапазон, включая форму необязательных параметров того же дага, — этап 5 (#7); туда же стык #111 про дату модельного мира и `logical_date`. - Инкрементальный ETL вместо пересборки — техдолг #2. -- Состав витрин `dm_transform` и звена DQ — #135 и нарезка этапа 4. +- Состав витрин `dm_transform` и звена DQ — [дока слоя DM](dm.md); + имена задач — нарезка этапа 4. ## Отклонённые варианты