# Часть 16. Витрина Customer 360 `mart_customer_360` — первая финальная витрина. Она объединяет атрибуты клиента, остатки на счетах и карточную активность. Это главный учебный пример: здесь видно, как спецификация продукта, ODCS, спецификация модели, dbt-тесты и проверочные факты должны работать вместе. ## Приёмочные факты - одна строка на `customer_id`; - `customer_id` not null и unique; - нет прямого PII; - `total_balance_rub` not null; - `risk_event_count_7d` not null; - обязательные поля контракта присутствуют; - lineage читается через `ref()`. ## Реализация Откройте: ```text models/marts/mart_customer_360.sql models/schema.yml specs/models/mart_customer_360.md specs/validation/customer_360.md ``` Проверьте, что SQL следует спецификации, а тесты покрывают ключевые факты. Не ограничивайтесь чтением SQL: утечка PII и дрейф контракта должны иметь отдельные проверки. ## Матрица приёмки | Факт | Доказательство | |---|---| | одна строка на `customer_id` | dbt-тест `unique` | | `customer_id` не пустой | dbt-тест `not_null` | | нет прямых PII | singular-тест схемы | | общий остаток присутствует | dbt-тест `not_null` | | счётчик риска присутствует | dbt-тест `not_null` | | поля контракта присутствуют | singular-тест колонок контракта | Если строка доказательства пустая, это не готовность, а пробел. ## Qwen-запрос ```text Сравни models/marts/mart_customer_360.sql, models/schema.yml, specs/customer_360_contract.odcs.yaml и specs/validation/customer_360.md. Покажи, какие приёмочные факты покрыты, какие ручные, а какие отсутствуют. Файлы не меняй. ``` ## Минимальный выход Подготовьте заметку ревьюера: ```markdown Гранулярность: одна строка на `customer_id`. PII: нет прямых PII. Поля контракта: `customer_id`, `total_balance_rub`, `risk_event_count_7d`. Проверки: `dbt build`, mart-тесты, список запрещённых PII, колонки контракта. Открытые вопросы: определение `risk_event_count_7d` зависит от входного риск-флага. ``` ## Разбор для читателя Customer 360 — хороший первый mart, потому что название знакомое и поэтому опасное. Почти каждый участник проекта думает, что понимает его смысл, но у разных людей это разные таблицы. Для аналитика это может быть срез клиента. Для маркетинга — профиль с сегментами. Для риска — агрегаты поведения. Для операционной команды — набор идентификаторов. SDD заставляет выбрать одну версию обещания и записать её до SQL. В этой книге Customer 360 намеренно ограничен. Одна строка на клиента. Прямые PII не раскрываются. Остаток считается из счетов. Риск-события берутся из синтетического окна активности. Такое ограничение может показаться бедным, но оно делает пример честным: каждое поле можно связать со спецификацией и проверкой. Если добавить всё сразу, студент увидит большую таблицу, но не увидит дисциплину продукта. Особое внимание нужно уделить `risk_event_count_7d`. Само имя обещает семидневное окно. Если SQL просто считает все риск-события, поле выглядит правдоподобно, но обманывает потребителя. Поэтому окно должно быть описано и проверено отдельным тестом. Это маленький пример большой идеи: название поля становится частью контракта. Если имя содержит срок, валюту, статус или уровень агрегации, проверка должна подтвердить именно это. Customer 360 также показывает, почему политику PII нельзя оставлять общей фразой. В raw и staging может быть `pii_email`, чтобы студент увидел риск. В mart его быть не должно. Это не вопрос эстетики схемы, а граница продукта. Ревьюер должен смотреть не только на итоговый `select`, но и на тест, который доказывает отсутствие прямых PII-полей. ## Практика Запустите только проверки Customer 360 и сопоставьте каждую строку вывода с матрицей приёмки. Если факт подтверждается только ручным ревью, так и запишите. ## Типичная ошибка Проверять только SQL глазами. В SDD нужно доказательство: dbt-тест, ревью схемы, явный проверочный факт или отчёт ревьюера. ## Контрольные вопросы 1. Почему `pii_email` не должен попасть в mart? 2. Какие поля делают витрину уровнем клиента? 3. Что изменится, если добавить детализацию по продукту?