Тема: Часть 14. Silver layer: клиенты, счета, карты, платежи
Уровень сложности: Средний
Расчётное время изучения: 2,5–3 часа (теория — 1 час, практика — 1 час, кейсы и повторение — 1 час)
Предварительные требования: Понимание архитектуры Medallion (Bronze/Silver/Gold) в Data Lakehouse
Базовые навыки работы с dbt: модели, ref(), sources, тесты
Знание SQL на уровне JOIN, GROUP BY, оконных функций
Представление о доменной области банковских данных: клиенты, счета, карты, платежи
Знакомство с понятиями grain, PII, lineage
Цели обучения: Объяснить роль Silver-слоя как переиспользуемого промежуточного уровня между staging и mart
Определять и формулировать grain (гранулярность) intermediate-моделей int_customer_balances и int_customer_card_activity
Применять правила silver-слоя: запрет на утечку PII, понятный grain, отсутствие продуктовых решений, использование ref() для lineage
Проводить приёмочную проверку silver-моделей по формализованным критериям качества
Заполнять таблицу соответствия grain / входы / использование / разрыв проверки для intermediate-моделей
Обзор: Silver layer в dbt-архитектуре банковской предметной области — это слой устойчивых промежуточных сущностей, который располагается между сырыми staging-моделями и финальными mart-витринами. Он устраняет техническую грязь источников (приведение типов, нормализация ключей, простейшие агрегации), но не принимает продуктовых решений, влияющих на бизнес-обещание. В учебном примере silver представлен двумя intermediate-моделями: int_customer_balances (агрегация счетов до уровня клиента) и int_customer_card_activity (агрегация карточных операций и сигналов риска). Главная ценность silver — переиспользование: одна и та же модель может читаться витриной Customer 360, кредитной витриной и витриной риска без дублирования логики. Однако эта переиспользуемость требует дисциплины: понятный grain, документированные окна для счётчиков, политика по PII и обязательное использование ref() для построения lineage.
Ключевые концепции: Silver layer (промежуточный слой): Слой в архитектуре Medallion, который создаёт устойчивые переиспользуемые сущности из сырых данных. Связывает staging (очистка и типизация источников) и mart (бизнес-витрины для потребителей). Не является финальным продуктом, но уже не сырым источником.
Средний-модель (int ): Тип модели в dbt, расположенный между staging и mart. Имеет префикс int_. Содержит промежуточные преобразования и агрегации. В данной части: int_customer_balances и int_customer_card_activity.
Grain (гранулярность): Ключевое свойство таблицы — одна строка на какую сущность (customer_id, account_id, transaction_id). Определяет, что именно считается записью в таблице. Модель с неочевидным grain становится непригодной для переиспользования.
Int customer balances: Промежуточная модель, агрегирующая данные из stg_accounts до уровня клиента. Grain: одна строка на customer_id. Используется в mart_customer_360. Содержит агрегированные балансы клиента по всем его счетам.
Int customer card activity: Промежуточная модель, агрегирующая операции по картам из stg_card_transactions и считающая сигналы уровня клиента, включая риск-счётчики (например, risk_event_count_7d). Grain: одна строка на customer_id.
Pii (personally identifiable information): Персональные данные клиента (ФИО, паспорт, телефон, email). Silver не должен пропускать прямой PII дальше, чем нужно, без политики обработки.
Ref(): Функция dbt, используемая для обращения к другим моделям в проекте. Создаёт явный lineage (родословную данных), который видит ревьюер в DAG. В silver все входные модели должны читаться только через ref().
Lineage (родословная данных): Граф зависимостей между моделями, который автоматически строится dbt на основе ref(). Позволяет понять, какие модели зависят от данной и какие источники она использует.
Риск-счётчик с понятным окном: Агрегат (например, risk_event_count_7d), где временное окно (7 дней) и определение риск-флага должны быть явно описаны в спецификации модели. Без этого модель непроверяема.
Переиспользуемость silver-слоя: Свойство, при котором одна и та же intermediate-модель может быть использована несколькими mart-витринами без дублирования логики. Снижает риск расхождений между витринами.
Разрыв проверки (validation gap): Ситуация, когда модель не имеет достаточной спецификации для проверки качества данных: не описано окно, не объяснено null-значение, не определён grain. Пустой разрыв проверки = модель достаточно описана.
Продуктовое решение: Бизнес-правило, определяющее, что считается «активным клиентом», «просроченным платежом», «кредитоспособным». Silver не должен принимать такие решения, если он переиспользуем — это работа mart-слоя.
Qwen-запрос для ревью: Специализированный промпт для LLM-ассистента, который сравнивает intermediate-модели со спецификациями и требованиями mart, выявляет grain, входы и разрывы проверки без изменения файлов.
Важные даты: Этап 1 — проектирование silver-слоя: Архитектурное решение о выделении промежуточных сущностей до создания mart-витрин. Не привязано к календарной дате, но является обязательной вехой проекта.
Этап 2 — согласование спецификаций: Момент, когда каждая int_-модель получает документированный grain, окна для счётчиков, политику по PII. До этого момента модель не считается готовой к merg'у.
Этап 3 — приёмочная проверка: Формальный прогон по чек-листу: ref() во входах, одна строка на customer_id для клиентских моделей, отсутствие необъяснённых null, понятные окна для риск-счётчиков.
Практические упражнения: Название: Построение таблицы соответствия для двух intermediate-моделей
Проблема: Используя материал Части 14, заполните таблицу grain / входы / используется в / разрыв проверки для моделей int_customer_balances и int_customer_card_activity. Для каждой модели укажите: (1) гранулярность — на какую сущность приходится одна строка; (2) входы — какие staging-модели она читает через ref(); (3) где используется — какие mart-модели её читают; (4) разрыв проверки — что не описано в спецификации (если всё описано — оставьте ячейку пустой).
Решение: Шаг 1. Определяем grain для int_customer_balances: модель агрегирует счета до уровня клиента, значит одна строка = один customer_id. Шаг 2. Входы по тексту: stg_accounts. Шаг 3. Используется в: mart_customer_360. Шаг 4. Разрыв проверки: в материале grain и входы указаны явно, окна не задействованы (агрегация балансов), PII не упоминается как проблемная зона — разрыв пуст. Шаг 5. Определяем grain для int_customer_card_activity: модель агрегирует карточные операции до уровня клиента, одна строка = один customer_id. Шаг 6. Входы: stg_card_transactions. Шаг 7. Используется в: mart_customer_360. Шаг 8. Разрыв проверки: модель считает risk_event_count_7d, но в спецификации должно быть объяснено, что такое «7 дней» (окно от какой даты, скользящее или фиксированное) и откуда берётся risk_flag (порог, источник). Если это не описано — разрыв есть. Итоговая таблица: см. раздел «Минимальный выход» в материале.
Сложность: intermediate
Название: Выявление продуктовых решений в silver-модели
Проблема: Вам передали SQL-код int_customer_card_activity, в котором есть строка: WHERE balance > 0 AND last_transaction_date > CURRENT_DATE - INTERVAL '90 days'. Коллега утверждает, что это просто фильтр «активности». Определите, нарушает ли это правила silver-слоя, и какие последствия это может иметь.
Решение: Шаг 1. Анализируем логику: balance > 0 — это бизнес-правило о том, что клиент «имеет средства». last_transaction_date > CURRENT_DATE - INTERVAL '90 days' — это бизнес-определение «активности» клиента. Шаг 2. Сопоставляем с правилами silver: silver не должен принимать продуктовых решений, если модель переиспользуема. Шаг 3. Делаем вывод: условие превращает silver-модель в продуктовое решение (определение «активного клиента»), что делает её непригодной для других mart-витрин, где определение активности может быть другим (например, кредитный скоринг может использовать 180 дней). Шаг 4. Рекомендация: убрать фильтр из int_-модели. Агрегаты (например, days_since_last_transaction, has_positive_balance) вынести как поля, а решение об «активности» принимать в mart-слое.
Сложность: intermediate
Название: Аудит lineage через ref()
Проблема: В проекте нашлась модель int_customer_card_activity_v2, которая читает stg_card_transactions напрямую через source(), а не через ref() к stg_card_transactions. Как это повлияет на ревью и какие риски это создаёт?
Решение: Шаг 1. Проверяем требования silver: «входные модели читаются только через ref()». Шаг 2. Что происходит при использовании source() вместо ref(): dbt не включит зависимость между int_customer_card_activity_v2 и stg_card_transactions в DAG проекта как model-to-model. Шаг 3. Последствия для ревьюера: ревьюер не увидит в lineage, что int_-модель зависит от stg_; не сможет отследить, какие mart-витрины будут затронуты при изменении источника; тесты на stg_card_transactions не будут автоматически распространяться на зависимые модели. Шаг 4. Рекомендация: исправить на ref('stg_card_transactions'), проверить, что модель попала в DAG (команда dbt ls --select int_customer_card_activity_v2 --output name).
Сложность: beginner
Название: Документирование риск-счётчика
Проблема: Модель int_customer_card_activity содержит поле risk_event_count_7d со значением 0 или целое число. Напишите спецификацию для этого поля, чтобы оно проходило приёмочную проверку silver-слоя.
Решение: Спецификация должна содержать: (1) Определение риск-события — например, «транзакция, помеченная флагом is_risk в stg_card_transactions» или «транзакция со score > 0.8 по модели антифрода». (2) Определение окна — «последние 7 календарных дней до даты выполнения модели (execution_date - 7 days)» или «7 дней до даты операции». (3) Тип скользящего окна — фиксированное или скользящее (rolling). (4) Источник риск-флага — stg_card_transactions.is_risk или отдельная таблица risk_scores. (5) Обработка null — если risk_score пуст, событие не считается (или считается, нужна явная инструкция). (6) Grain итогового значения — число событий на одного customer_id за окно. Без такой спецификации модель похожа на SQL, но не на устойчивый дата-актив.
Сложность: intermediate
Название: Рефакторинг широкой int_-модели
Проблема: В репозитории нашлась модель int_customer_universe, которая содержит: клиентские балансы, агрегаты карточных операций, флаги продуктов, статус AML-проверки и согласия клиента. Объясните, почему это нарушает правила silver-слоя, и предложите план рефакторинга.
Решение: Шаг 1. Проверяем правила: silver не должен смешивать несвязанные домены и должен иметь понятный grain и одно назначение. Шаг 2. Анализируем модель: внутри смешаны минимум три домена — клиентские финансы (балансы), поведенческие сигналы (карточные операции), комплаенс (AML, согласия). Каждый из них имеет свою динамику обновления, своих владельцев и свои mart-потребители. Шаг 3. Один grain (customer_id) формально есть, но одно назначение отсутствует: модель пытается быть универсальной шиной для всех mart-витрин сразу. Шаг 4. План рефакторинга: разделить на три int_-модели — int_customer_balances, int_customer_card_activity, int_customer_compliance (с AML и согласиями). Если mart-витрине Customer 360 нужны все три — она сама делает JOIN через ref(). Шаг 5. Проверяем по критерию из материала: «помогает ли слой выразить повторяемую сущность и доказать её качество?». После разделения — да; до разделения — нет.
Сложность: advanced
Кейсы: Название: Кейс: расхождение «активных клиентов» между витринами риска и CRM
Сценарий: Банк из топ-20 (условный пример) построил Data Lakehouse на dbt. Silver-слой содержал модель int_customer_activity, которая агрегировала операции и счета на уровне customer_id. От неё зависели две mart-витрины: mart_risk_scoring (для кредитного скоринга) и mart_crm_active_customers (для маркетинговых кампаний). В int_customer_activity был зашит фильтр: customer_id IN (SELECT customer_id FROM stg_accounts WHERE balance > 0).
Задача: Через полгода продуктовая команда риск-скоринга запросила пересчёт с другим определением активности (с учётом кредитных лимитов, без требования положительного баланса). Параллельно команда CRM попросила ужесточить фильтр (только клиенты с операциями за 30 дней). Стало ясно, что silver-модель не переиспользуема — каждая mart-витрина вынуждена была создавать свою копию логики. Возникли расхождения: mart_risk_scoring_v1 показывал 1,2 млн «активных», mart_crm_active_customers_v1 — 800 тыс., а mart_credit_eligibility_v2 (новая витрина) — 950 тыс. Аналитики спорили, какая цифра правильная, аудиторы фиксировали нарушение принципа единого источника правды.
Решение: Команда данных провела рефакторинг silver-слоя по принципам, описанным в Части 14. Шаг 1: int_customer_activity был удалён. Шаг 2: созданы две разделённые int_-модели — int_customer_balances (агрегаты балансов, без фильтров «активности») и int_customer_card_activity (агрегаты операций с документированным окном risk_event_count_7d). Шаг 3: для mart_risk_scoring добавлено поле has_positive_balance как атрибут, а фильтр активности перенесён в сам mart. Шаг 4: для mart_crm_active_customers аналогично — days_since_last_transaction перенесён как атрибут, а бизнес-фильтр «30 дней» живёт в mart. Шаг 5: для mart_credit_eligibility — своё определение в mart. Шаг 6: проведён приёмочный прогон по чек-листу silver — ref() во входах, одна строка на customer_id, нет необъяснённых null, окна для счётчиков задокументированы.
Результат: Через 2 месяца после рефакторинга: (1) три mart-витрины читают одни и те же silver-модели, расхождения в определении «активности» стали явными и обсуждаемыми; (2) время на изменение логики активности сократилось с 3 недель (когда правили три копии) до 3 дней (когда меняется только mart); (3) аудит прошёл без замечаний по принципу единого источника правды; (4) lineage в dbt стал прозрачным — любое изменение в int_customer_card_activity видно во всех зависимых mart.
Извлечённые уроки: Silver не должен содержать продуктовых фильтров — иначе он перестаёт быть переиспользуемым
Расхождения между mart-витринами — симптом зашитых бизнес-правил в silver
Принцип «одна модель — одно назначение — один grain» важнее архитектурной красоты
Документирование окон и источников флагов обязательно для прохождения аудита
Связанные концепции: Grain
Переиспользуемость silver-слоя
Продуктовое решение в silver
Lineage через ref()
Разделение ответственности между silver и mart
Название: Кейс: утечка PII через переиспользуемую silver-модель
Сценарий: Финтех-компания (пример на основе реальных инцидентов в индустрии) построила int_customer_profile для агрегации клиентских данных. Модель читала stg_customers, содержащую ФИО, email, телефон, паспортные данные, дату рождения, и агрегировала всё это в одну строку на customer_id. Модель была удобной — её подключили к пяти mart-витринам, включая mart_marketing_segments (для внешней рассылки).
Задача: Служба информационной безопасности при плановом аудите обнаружила, что mart_marketing_segments экспортируется в маркетинговую платформу с полным ФИО и паспортом клиента, хотя для сегментации нужен только хэш email и возрастная группа. Формально нарушения не было — данные «согласованы» при онбординге. Но политика обработки PII требовала минимизации: каждая mart-витрина должна получать только те PII, которые обоснованы её назначением. Передача паспорта в маркетинг была признана избыточной.
Решение: Команда данных применила правило из Части 14: «не раскрывать прямые PII дальше, чем нужно». Шаг 1: int_customer_profile была разделена на int_customer_identity (ФИО, паспорт — только для комплаенс-mart) и int_customer_attributes (возрастная группа, сегмент, хэш email — для всех остальных). Шаг 2: в модели явно прописана политика: «поля *_pii доступны только для моделей с тегом compliance_use_case». Шаг 3: mart_marketing_segments теперь читает int_customer_attributes, где есть только хэш email. Шаг 4: проведён приёмочный прогон — пункт «PII не утекает в переиспользуемый слой без политики».
Результат: Аудит ИБ закрыт без замечаний. Маркетинговая платформа получает минимально необходимый набор данных. Комплаенс-mart сохраняет доступ к полному PII через отдельную защищённую ветку. Время на onboard нового потребителя mart сократилось — больше не нужно вручную фильтровать поля в int_customer_profile.
Извлечённые уроки: PII в silver — это не технический, а политический вопрос: каждая mart должна иметь обоснование
Широкая модель с PII — антипаттерн переиспользуемого silver-слоя
Разделение PII и атрибутов должно происходить до масштабирования количества mart
Теги dbt (tags) помогают автоматизировать контроль за PII-политиками
Связанные концепции: PII
Политика обработки персональных данных
Переиспользуемость silver-слоя
Grain
Теги в dbt
Советы по изучению: Держите под рукой схему Medallion-архитектуры: рисуйте стрелки Bronze → Silver → Gold на каждом примере, чтобы не путать слои
Запомните триаду silver-модели: grain + одно назначение + документированные окна. Если не хватает хотя бы одного элемента — модель не готова
При чтении чужого int_-файла в dbt всегда проверяйте три вещи: (1) ref() во входах, (2) наличие тестов на уникальность ключа grain, (3) наличие описания в properties.yml
Не измеряйте качество silver количеством моделей. Одна аккуратная int_-модель лучше трёх слоёв с красивыми именами — это ключевая мысль Части 14
Используйте Qwen-запрос из материала как шаблон: «Сравни intermediate-модели со спецификациями и требованиями mart. Покажи grain, входы ref(), переиспользуемые поля и разрывы проверки»
При подготовке к ревью научитесь объяснять каждое поле int_-модели за 30 секунд: что это, откуда, зачем нужно mart-потребителям
Делайте таблицу grain/входы/используется в/разрыв проверки для каждой новой int_-модели до начала разработки — это дисциплинирует
Избегайте соблазна «немного улучшить» silver — добавить фильтр, переименовать риск, посчитать удобный признак. Каждое такое улучшение должно быть записано в спецификации
Дополнительные ресурсы: Dbt documentation — intermediate models: https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview (раздел про staging → intermediate → marts)
Dbt documentation — ref() function: https://docs.getdbt.com/reference/dbt-jinja-functions/ref
Dbt documentation — graph operators и lineage: https://docs.getdbt.com/reference/dbt-jinja-functions/graph-operators
Data quality в dbt — тесты и properties.yml: https://docs.getdbt.com/best-practices/warnings
The medallion architecture (databricks): https://www.databricks.com/glossary/medallion-architecture
Dbt tags для политик доступа: https://docs.getdbt.com/reference/resource-configs/tags
Qwen (tongyi qianwen) — llm для ревью dbt-кода: https://qwen.alibaba.com/
Книга 'data mesh' (zhamak dehghani): Концепция data-as-a-product перекликается с принципом «одна int_-модель — одно назначение»
Резюме: Silver layer в архитектуре банковских данных (клиенты, счета, карты, платежи) — это слой устойчивых переиспользуемых сущностей, который устраняет техническую грязь источников, но не принимает продуктовых решений. В учебном примере он представлен двумя intermediate-моделями: int_customer_balances (агрегация счетов до уровня клиента) и int_customer_card_activity (агрегация карточных операций и риск-счётчиков). Главные правила silver: понятный grain (одна строка на customer_id для клиентских моделей), использование ref() для построения lineage, запрет на утечку PII без политики, документированные окна для счётчиков (например, что такое «7 дней» в risk_event_count_7d), отсутствие смешения несвязанных доменов. Критерий качества silver простой: помогает ли слой выразить повторяемую сущность и доказать её качество? Если нет — модель создана ради архитектурной симметрии, а не ради читателя или потребителя данных. Для самопроверки используйте таблицу grain / входы / используется в / разрыв проверки и контрольные вопросы: почему silver не должен быть привязан к одному продукту, какие PII нельзя переносить без политики, как ref() помогает ревьюеру.