Анатомия dbt-проекта
После dbt init у нас есть skeleton проекта. Этот урок проходит по каждой папке и файлу: что это, зачем оно, и когда вы будете с этим работать.
После этого урока вы будете понимать каждую строчку, которую увидите в любом dbt-проекте — будь то Jaffle Shop tutorial или production-проект из 2000 моделей.
Карта проекта
Звёздочка — то, с чем работаете каждый день. (gen) — то, что генерируется, и в git не коммитится.
dbt_project.yml: главный конфиг
Это manifest проекта. По нему dbt понимает, что у него есть.
Реальный полный пример после очистки:
name: 'jaffle_shop'
version: '1.0.0'
profile: 'jaffle_shop'
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
clean-targets:
- "target"
- "dbt_packages"
vars:
start_date: '2026-01-01'
models:
jaffle_shop:
+materialized: view
staging:
+materialized: view
intermediate:
+materialized: ephemeral
marts:
+materialized: table
seeds:
jaffle_shop:
raw_customers:
+column_types:
customer_id: integer
snapshots:
jaffle_shop:
+target_schema: snapshots
tests:
+severity: warn
Разберём ключевые секции:
name, version
Имя проекта (snake_case) и semver-версия. name важен — он определяет namespace для всех config’ов ниже (models.jaffle_shop.*).
profile
Имя profile в profiles.yml. Часто совпадает с name, но может отличаться (полезно для shared profiles между проектами).
*-paths
Где dbt ищет соответствующие файлы. Стандартные значения:
| Поле | По умолчанию |
|---|---|
model-paths | ["models"] |
analysis-paths | ["analyses"] |
test-paths | ["tests"] |
seed-paths | ["seeds"] |
macro-paths | ["macros"] |
snapshot-paths | ["snapshots"] |
Можно указать несколько путей: model-paths: ["models", "vendor_models"]. Но в 99% проектов оставляют дефолт.
clean-targets
Что удаляет команда dbt clean. Стандартно — target/ (compiled) и dbt_packages/ (installed packages).
vars
Project-level переменные. Доступны в моделях через {{ var('start_date') }}. Можно overridе на CLI: dbt run --vars '{"start_date": "2026-06-01"}'.
models
Самая важная секция. Конфиги по подпапкам:
models:
jaffle_shop: # имя проекта
+materialized: view # default для ВСЕХ моделей проекта
staging: # для models/staging/*
+materialized: view
intermediate: # для models/intermediate/*
+materialized: ephemeral
marts: # для models/marts/*
+materialized: table
Префикс + означает «применить как config к моделям внутри». Без + это nested-namespace (например, models.jaffle_shop.marketing.campaigns — namespace для models/marketing/campaigns/).
dbt применяет конфиги иерархически: модель models/marts/finance/orders.sql получает:
- Project-level
+materialized: view(default) - Перебивает
marts.+materialized: table - И уже это перекрывает
{{ config(materialized='incremental') }}внутри.sqlмодели
Конфигов, которые задаются в dbt_project.yml, много: +schema, +tags, +meta, +pre-hook, +post-hook, +materialized, +on_schema_change, +grants, и так далее. Их разбираем в соответствующих модулях.
Прочие секции
seeds:— конфиги для CSV-файловsnapshots:— для snapshot-определенийtests:— для тестов (severity, store_failures)dispatch:— для macro dispatch (см. модуль про macros)quoting:— управление цитированием идентификаторов в SQL
models/: где живут SQL-модели
Самая важная папка. Тут весь ваш business logic.
Типичная структура production-проекта:
models/
├── staging/
│ ├── stripe/
│ │ ├── _stripe__sources.yml # source declarations
│ │ ├── _stripe__models.yml # tests + docs
│ │ ├── stg_stripe__payments.sql
│ │ └── stg_stripe__customers.sql
│ └── salesforce/
│ ├── _salesforce__sources.yml
│ └── stg_salesforce__accounts.sql
├── intermediate/
│ ├── int_payments_with_customers.sql
│ └── int_monthly_revenue.sql
└── marts/
├── core/
│ ├── dim_customers.sql
│ ├── fct_orders.sql
│ └── _core__models.yml
└── finance/
├── fct_invoices.sql
└── _finance__models.yml
Три слоя — staging / intermediate / marts — это de facto стандарт от dbt Labs. Модуль 13 курса разбирает это подробно. Сейчас достаточно знать:
- staging — лёгкая нормализация raw source-данных (rename, cast, очистка nulls)
- intermediate — joins, агрегации, бизнес-логика, не предназначенная для прямого потребления
- marts — финальные таблицы для аналитики, организованы по доменам (core, finance, marketing)
Файлы внутри:
.sql— модель. Содержит SELECT и опциональный{{ config() }}блок..yml(с префиксом_<подпапка>__sources.ymlили_<подпапка>__models.yml— конвенция) — описание sources, tests, docs.
Подчёркивание _ в имени YAML-файла — это конвенция, чтобы файл шёл первым в alphabetical sort и не путался среди .sql. dbt не требует этого формально, но 95% проектов так делают.
seeds/: CSV -> таблицы
CSV-файлы, которые dbt загружает в warehouse как таблицы через dbt seed.
Типичные use cases:
- Маленькие справочные таблицы: страны, типы продуктов, конфигурация
- Тестовые данные для CI
- Маппинги: account_id -> segment
Пример seeds/countries.csv:
country_code,country_name,continent
US,United States,North America
GB,United Kingdom,Europe
DE,Germany,Europe
JP,Japan,Asia
После dbt seed в warehouse появится таблица main.countries с тремя колонками. В модели можно использовать как {{ ref('countries') }}.
Конфиг в dbt_project.yml:
seeds:
jaffle_shop:
countries:
+column_types:
country_code: varchar(2)
country_name: varchar(64)
Без column_types dbt инферит типы из содержимого CSV, что иногда даёт некорректные типы (например, числовые ID как BIGINT вместо VARCHAR).
Не кладите большие CSV (>5MB) в seeds/. git будет тормозить. Большие справочники грузите через Fivetran/Airbyte как обычные sources.
snapshots/: SCD2 поверх source
Snapshot — это way to track changes over time. Подробно в модуле 13. Пример:
{% snapshot orders_snapshot %}
{{
config(
target_schema='snapshots',
strategy='timestamp',
unique_key='order_id',
updated_at='updated_at',
)
}}
select * from {{ source('jaffle_shop', 'orders') }}
{% endsnapshot %}
Что это делает: каждый прогон dbt snapshot сравнивает текущее состояние source.orders с предыдущим snapshot. Изменённые строки помечаются dbt_valid_to (когда были актуальны), новые версии получают dbt_valid_from. На выходе — Slowly Changing Dimension Type 2.
Snapshots живут в отдельной папке snapshots/, а не в models/, потому что у них особенная семантика (они мутируют состояние, в отличие от моделей, которые re-create).
tests/: singular tests
В этой папке — singular tests. Это отдельные SQL-запросы, которые должны возвращать 0 строк (если хоть одна строка — тест провален).
Пример tests/no_negative_revenue.sql:
-- Тест: в fct_orders не должно быть отрицательных revenue
select
order_id,
revenue
from {{ ref('fct_orders') }}
where revenue < 0
Запускается через dbt test. Если в fct_orders есть строки с revenue менее 0 — тест fail.
Singular tests — для custom business rules, которые сложно выразить через generic tests (not_null, unique, и так далее). Подробно в модуле 9.
macros/: переиспользуемая логика
Jinja-макросы. Эквивалент функций в Python.
Пример macros/cents_to_dollars.sql:
{% macro cents_to_dollars(amount_in_cents) %}
({{ amount_in_cents }} / 100.0)::numeric(16, 2)
{% endmacro %}
Использование в модели:
select
order_id,
{{ cents_to_dollars('amount_in_cents') }} as amount_in_dollars
from {{ ref('stg_orders') }}
При компиляции {{ cents_to_dollars('amount_in_cents') }} развернётся в (amount_in_cents / 100.0)::numeric(16, 2).
Macros используются для:
- Переиспользуемых SQL-фрагментов (как пример выше)
- Cross-adapter compatibility (разные функции на Postgres vs Snowflake)
- Кастомных generic tests
- Управления generate_schema_name / generate_alias
Подробно в модуле 11.
analyses/: ad-hoc запросы
Файлы .sql в analyses/ — это запросы, которые компилируются, но не выполняются в dbt run. Они доступны через dbt compile и можно посмотреть в target/compiled/.
Пример analyses/total_revenue_2026.sql:
select
date_trunc('month', order_date) as month,
sum(revenue) as total_revenue
from {{ ref('fct_orders') }}
where order_date >= '2026-01-01'
group by 1
order by 1
После dbt compile:
cat target/compiled/jaffle_shop/analyses/total_revenue_2026.sql
Получите готовый SQL, в котором {{ ref('fct_orders') }} подставлен на dev.main.fct_orders. Этот SQL можно скопировать в Tableau / Looker / для ad-hoc запроса в warehouse.
В реальной работе analyses используются редко. Большинство ad-hoc запросов проще делать прямо в SQL editor warehouse.
target/: compiled SQL + metadata
Папка, которую dbt создаёт автоматически каждый dbt run. Никогда не редактируется вручную. Не коммитится в git.
Что внутри:
target/
├── compiled/
│ └── jaffle_shop/
│ └── models/
│ └── marts/
│ └── orders.sql # Compiled SQL с подставленными ref()
├── run/
│ └── jaffle_shop/
│ └── models/
│ └── marts/
│ └── orders.sql # Compiled SQL ОБЁРНУТЫЙ в CREATE TABLE/MERGE
├── manifest.json # Граф всего проекта
├── catalog.json # Схемы warehouse (от dbt docs generate)
├── run_results.json # Результаты последнего run/test
├── partial_parse.msgpack # Кэш парсинга для ускорения
└── graph.gpickle # Сериализованный DAG
Главные файлы:
| Файл | Что внутри |
|---|---|
target/compiled/<project>/<path>/<model>.sql | Чистый SQL без CREATE wrapper. Полезно для понимания “что я написал” |
target/run/<project>/<path>/<model>.sql | Тот же SQL, обёрнутый в CREATE TABLE / CREATE VIEW / merge logic |
manifest.json | Полный граф проекта: все модели, sources, tests, exposures, их зависимости |
run_results.json | Результаты последнего run: статусы, timings, errors |
catalog.json | Информация о схемах из warehouse (генерируется dbt docs generate) |
Разница compiled vs run — частый источник путаницы. Это разбираем в следующем уроке.
dbt_packages/: установленные пакеты
После dbt deps сюда устанавливаются packages из packages.yml. Эквивалент node_modules/.
dbt_packages/
├── dbt_utils/
│ ├── dbt_project.yml
│ └── macros/
│ ├── generic_tests/
│ ├── sql/
│ └── ...
├── dbt_expectations/
└── codegen/
Каждый package — это отдельный dbt-проект, со своими macros/, models/, dbt_project.yml. Macros из packages доступны в вашем проекте через namespace: {{ dbt_utils.generate_surrogate_key([...]) }}.
В git не коммитится. Восстанавливается через dbt deps.
logs/: журнал запусков
В logs/dbt.log идёт детальный лог каждого запуска dbt:
2026-05-19 06:30:11.823 -0500 [INFO] Running with dbt=1.10.3
2026-05-19 06:30:11.823 -0500 [DEBUG] running dbt with arguments {...}
2026-05-19 06:30:11.823 -0500 [DEBUG] Sending event: {...}
...
2026-05-19 06:30:12.114 -0500 [DEBUG] On model.jaffle_shop.my_first_dbt_model: BEGIN
2026-05-19 06:30:12.117 -0500 [DEBUG] SQL:
create table "dev"."main"."my_first_dbt_model__dbt_tmp" as
(with source_data as ...)
Полезно для debug-сессий: что именно dbt отправил в warehouse, как трактовал config, сколько занял каждый шаг.
В git не коммитится. Можно очищать через rm -rf logs/ — пересоздастся.
profiles.yml (вне проекта)
В ~/.dbt/profiles.yml. Подробно — следующий урок. Сейчас достаточно знать, что:
- Это отдельный файл, не в папке проекта
- Хранит credentials для warehouse
- Связан с проектом через имя
profileвdbt_project.yml - Не коммитится в git (хранится в home-директории)
.gitignore: что НЕ коммитить
Стандартный набор:
# Auto-generated
target/
dbt_packages/
logs/
# DuckDB local file
*.duckdb
*.duckdb.wal
*.duckdb.tmp
# Python venv
.venv/
__pycache__/
*.pyc
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
dbt 1.10 init не создаёт .gitignore автоматически в проекте — добавьте сами.
packages.yml (опционально)
Если используете внешние пакеты — создайте packages.yml в корне:
packages:
- package: dbt-labs/dbt_utils
version: 1.4.0
- package: calogica/dbt_expectations
version: 0.11.0
После этого:
dbt deps
скачает пакеты в dbt_packages/. Подробно в модуле 16.
Что НЕ должно быть в проекте
Чтобы закрыть тему — несколько вещей, которые часто ошибочно кладут в dbt-проект, но не должны:
- Sample CSV для тестирования — кладите в
seeds/, но только если они малы (меньше 5MB). Большие — в отдельный data layer. - Скрипты для загрузки данных в warehouse (Python для Stripe API) — это E+L, отдельный инструмент (Fivetran/Airbyte/custom Airflow DAG). Не место в dbt.
- Скрипты для отправки emails / Slack — это оркестрация, в Airflow или dbt Cloud Jobs.
- Tableau workbooks / Looker LookML — в своих репозиториях BI-инструментов.
- Документация по бизнес-процессам компании — в Confluence/Notion. В dbt — только техническая документация про модели.
Попробуй сам
Возьмите свой dbt-проект из предыдущего урока:
- Откройте
dbt_project.yml. Найдите секциюmodels:. Удалитеexample:(мы её уже убрали). - Добавьте структуру:
models: jaffle_shop: +materialized: view staging: +materialized: view intermediate: +materialized: ephemeral marts: +materialized: table - Создайте папки:
mkdir -p models/staging models/intermediate models/marts - Запустите
dbt parse— это валидация YAML без выполнения. Должно пройти без ошибок. - Запустите
dbt list --select staging— пустой список (моделей в staging пока нет, но команда не падает). - Посмотрите
target/manifest.json:cat target/manifest.json | head -50. Большой JSON с описанием каждой ноды графа. На этом этапе моделей нет, но структура уже видна.
После этого ваш проект — production-ready skeleton, готовый принять реальные модели.
git init: устройство репозитория