# Учебные задания: витрины слоя DM и дашборд в Superset Пять домашних заданий — про витрины слоя DM. Стенд привозит слой наполовину: витрины трех сюжетов дашборда и контроль качества построены, остальные витрины строит менти. Каждое из них — самостоятельное ТЗ: что за витрина, какое у нее зерно, из чего она собирается и как проверить, что собралась верно. Шестое задание — необязательная лаба в Superset. В ней менти собирает свой дашборд поверх пяти витрин. Общее устройство слоя — [дока DM](../architecture/dm.md). Она отвечает на вопросы «почему так», задание отвечает на вопрос «что сделать». ## Порядок Задания идут по нарастанию: каждое следующее опирается на то, что уже усвоено. | № | Результат | Чему учит | |---|---|---| | 1 | [`events_enriched_v`](events-enriched.md) | слой — это контракт, а не копия данных | | 2 | [`top_pages_daily`](top-pages-daily.md) | зерно сменилось — витрина материализуется и встает в конвейер | | 3 | [`session_overview`](session-overview.md) | зерно решает, на какие вопросы витрина уже не ответит | | 4 | [`dq_errors_daily`](dq-errors-daily.md) | у брака свой день и свой срок жизни | | 5 | [`utm_effectiveness`](utm-effectiveness.md) | `declared_` в имени — предупреждение, а не украшение | | 6 | [Свой дашборд в Superset](superset-dashboard.md) — необязательная лаба | свои объекты BI живут отдельно от стендовых | ## Общие правила Они одни для пяти заданий по витринам, поэтому записаны здесь, а не повторяются в каждом ТЗ. **Читать — через `_v`.** Публичный договор сущности DDS — представление с суффиксом `_v`: `dds.event_v`, `dds.session_v`. Витрина называет только его; физические `_rep` и `_dist` служат загрузке и обслуживанию. Соседнюю домашку витрина источником тоже не берет: витрина слоя DM читает договор DDS, и задания остаются независимыми. Исключение одно и оно оговорено в задании 4: у таблиц брака публичного договора нет. **Писать — только в `_dist`.** При записи в локальную таблицу раскладку определяет то, какая нода случайно выполняла код, а не свойство данных. Правило и его следствия — [дока хранилища](../architecture/storage.md), «Раскладка по шардам». **Форма — по зерну.** Витрина, меняющая зерно источника, материализуется: `dm.<имя>_rep` / `_dist`, донор `dm.<имя>_stage_rep` / `_stage_dist` и публичное `dm.<имя>_v` с явным списком колонок. Витрина, сохраняющая зерно, остается обычным представлением. Довод — [дока DM](../architecture/dm.md), «Форма: по зерну, не по цене запроса». **Ключ шардирования — твое решение.** Ни одно ТЗ ключа не называет. Правило выбора и требование к донору — [дока хранилища](../architecture/storage.md), «Раскладка по шардам»: донор объявляется через `AS <цель>_rep` и шардируется тем же ключом, что цель, иначе замена партиции потеряет строку на одном шарде и задвоит на другом. Названный ключ надо уметь защитить: почему он не дает перекоса и почему более сложный тут ничего не добавляет. Образец такой защиты — `dm.revenue_daily_dist` в [`sql/ddl/55-dm-tables.sql`](../../sql/ddl/55-dm-tables.sql). **Где лежит код.** DDL домашек — свой файл `sql/ddl/70-labs.sql`: он применяется после стендовых по порядку имени, и твои объекты не мешаются со стендовыми. У агрегатов рядом со стендовыми ложатся еще три запроса — `sql/dm/<витрина>_scope.sql` спрашивает у хранилища, какие дни пересобирает запуск, `_rebuild.sql` собирает их в донор, `_replace.sql` заменяет партицию, — и задача в [`dags/dm_transform.py`](../../dags/dm_transform.py). У разминочного представления нет ничего из этого: весь его код — одно `CREATE VIEW`. **Образец на все случаи** — витрина `revenue_daily`: у нее есть все пять объектов, донор, сборка, замена дневных партиций и задача в конвейере. Списывать с нее устройство можно и нужно; списывать состав колонок — нет, он у каждой витрины свой. ## Самопроверка Даг `homework_check` запускается рукой, расписания у него нет. Задача витрины красная, пока витрины нет или счет не сходится, и зеленая, когда собрано верно. В сводку качества конвейера `dm.dq_summary` он не пишет: это инструмент лабы, а не история качества стенда. Проверка пяти заданий по витринам устроена одинаково и золотых констант не содержит: рядом с витриной независимым запросом собирается тот же ответ — прямо из источника витрины, минуя ее саму. У четырех заданий источник эталона тот же, что у витрины, `dds.session_v` или `dds.event_v`; у задания 4 это таблицы брака ODS, потому что в DDS у брака представительства нет. Обе стороны соединяются по деловому ключу через `FULL OUTER JOIN`, и считается число строк, где стороны разошлись или одной стороны нет. Ноль — зеленый. Полное соединение здесь обязательно: при обычном пропавший день или пропавший ключ просто не попал бы в результат и остался бы незамечен. Раздел «Как проверить себя» каждого ТЗ по витрине называет деловой ключ и сравниваемые величины. Тот же прием в стендовом исполнении — [даг `dq_check`](../../dags/dq_check.py) и запросы в [`sql/dq/`](../../sql/dq). **Предупреждение.** Запросы самой проверки лежат в `sql/dq/` открыто, и имена называют витрину и роль сверки. Проверка на инвариантах неизбежно близка к ответу: чтобы сверить витрину, надо ее пересчитать. Прятать эти файлы никто не станет — но открыв их до того, как задание сделано, ты обменяешь урок на готовый SQL. Смотреть туда стоит после своей сборки: сравнить два решения полезно, списать одно — нет. ## Уборка Черновики и опыты за собой сносить: правило проверок стенда — [дока тестирования](../architecture/testing.md). Снесенная витрина обязана снова покраснить свою задачу в `homework_check` — если не покраснила, проверка ничего и не проверяла.