Best practices и реальный пример витрин
Соберём всё вместе: правила хорошего проекта и сквозной пример от сырья до витрины.
Идемпотентность — свойство, при котором повторный запуск пайплайна даёт тот же результат; вместе с DRY, слоями и неймингом это фундамент здорового dbt-проекта.
Лучшие практики
| Практика | Почему важно |
Нейминг по слоям (stg_, int_, fct_, dim_) | Из имени ясно, что за модель и где она в графе |
| Слои staging→intermediate→marts | Переиспользование чистки, порядок, читаемость |
| Идемпотентность | Повторный запуск безопасен, нет накопления мусора |
| DRY через макросы и staging | Логика в одном месте — правишь однажды |
Тесты на ключи (unique+not_null) | Ловит самые опасные баги данных рано |
| Описания в YAML | Документация не отстаёт от кода |
Типичные ошибки (антипаттерны)
- Нет тестов. Модели строятся, но никто не знает, корректны ли данные.
- Монолитные модели. Одна гигантская модель на 500 строк SQL с десятком джойнов — не тестируется, не переиспользуется. Дробите на слои.
- Неверная материализация. view для тяжёлой витрины (медленный BI) или table для всего (лишнее место); инкрементал без
is_incremental(). - Хардкод имён таблиц вместо
ref()/source(). Ломает DAG и переносимость. - Бизнес-логика, размазанная по marts. Сложное выносят в intermediate.
dbt против обычных SQL-скриптов и против Airflow
| Голые SQL-скрипты | dbt | Airflow | |
| Порядок запуска | Вручную | Из DAG автоматически | Задаёт оркестрацию шагов |
| Тесты данных | Нет | Встроены | Не его задача |
| Документация/lineage | Нет | Авто | Нет |
| Зона ответственности | Разрозненные запросы | Трансформация (T) | Расписание и порядок шагов |
dbt не конкурирует с Airflow — он живёт внутри шага оркестрации, заменяя хаос из скриптов на управляемый граф трансформаций.
Сквозной пример: витрина выручки по странам
Пройдём путь от сырья до витрины. Слой staging чистит источники:
-- models/staging/stg_orders.sql
select
id as order_id,
customer_id,
cast(amount_cents as numeric) / 100.0 as amount,
date(created_at) as order_date
from {{ source('raw', 'orders') }}
-- models/staging/stg_customers.sql
select id as customer_id, country_code
from {{ source('raw', 'customers') }}
Слой intermediate соединяет и подтягивает справочник стран (seed):
-- models/intermediate/int_orders_enriched.sql
select
o.order_id, o.amount, o.order_date,
cc.country_name
from {{ ref('stg_orders') }} o
join {{ ref('stg_customers') }} c on o.customer_id = c.customer_id
join {{ ref('country_codes') }} cc on c.country_code = cc.code
Слой marts — итоговая витрина (таблица):
-- models/marts/fct_revenue_by_country.sql
{{ config(materialized='table') }}
select
order_date,
country_name,
sum(amount) as revenue,
count(*) as orders_count
from {{ ref('int_orders_enriched') }}
group by order_date, country_name
Проверим финальную агрегацию на песочнице
CREATE TABLE int_orders (order_date TEXT, country TEXT, amount NUMERIC);
INSERT INTO int_orders VALUES
('2026-06-01','Россия',100),('2026-06-01','Россия',150),
('2026-06-01','Казахстан',80),('2026-06-02','Россия',200);
select order_date, country,
sum(amount) as revenue, count(*) as orders_count
from int_orders
group by order_date, country
order by order_date, country;
Вывод:
2026-06-01 | Казахстан | 80 | 1 2026-06-01 | Россия | 250 | 2 2026-06-02 | Россия | 200 | 1
Тесты и документация витрины
models:
- name: fct_revenue_by_country
description: "Выручка и число заказов по дате и стране."
columns:
- name: revenue
description: "Суммарная выручка в рублях."
tests: [not_null]
- name: country_name
tests: [not_null]
# Построить и протестировать весь пайплайн
dbt build
Итоги
- Здоровый проект: нейминг по слоям, staging→intermediate→marts, идемпотентность, DRY, тесты на ключи, описания в YAML.
- Антипаттерны: нет тестов, монолитные модели, неверная материализация, хардкод вместо
ref(). - dbt заменяет хаос SQL-скриптов управляемым DAG трансформаций и живёт внутри шага оркестрации (Airflow), а не вместо него.
- Сквозной пример: source → stg → int (+seed) → fct, затем тесты и
dbt build.
Проверьте себя
1. Что такое идемпотентность пайплайна dbt?
AСкорость выполнения
BПовторный запуск даёт тот же результат без накопления мусора
CШифрование данных
DКоличество моделей
2. Какой из этих пунктов — антипаттерн в dbt?
AТесты unique+not_null на ключах
BСлои staging→intermediate→marts
CМонолитная модель на 500 строк с десятком джойнов
DОписания моделей в YAML
3. Как соотносятся dbt и Airflow?
AЭто конкуренты, выбирают один
Bdbt живёт внутри шага оркестрации Airflow, отвечая за трансформацию (T), а Airflow — за расписание и порядок шагов
CAirflow — часть dbt
DЭто одно и то же