Learning Platform
Глоссарий Troubleshooting
Урок 04.03 · 22 мин
Начальный
project-structuredbt_project.ymlanatomy

Анатомия dbt-проекта

После dbt init у нас есть skeleton проекта. Этот урок проходит по каждой папке и файлу: что это, зачем оно, и когда вы будете с этим работать.

После этого урока вы будете понимать каждую строчку, которую увидите в любом dbt-проекте — будь то Jaffle Shop tutorial или production-проект из 2000 моделей.


Карта проекта

Анатомия dbt-проекта
dbt_project.ymlГлавный manifest проекта: name, version, profile, paths, конфиги моделей. Один на проект.
profiles.ymlЛежит ВНЕ проекта в ~/.dbt/profiles.yml. Connection credentials для warehouse.
packages.ymlОпциональный. Список пакетов экосистемы (dbt_utils, dbt_expectations).
код проекта
models/.sql и .yml файлы для моделей. Главный артефакт проекта.
seeds/CSV-файлы, которые превращаются в таблицы через dbt seed.
snapshots/.sql файлы snapshot-определений. SCD2 поверх source-таблиц.
tests/Singular tests — отдельные SQL файлы с пользовательскими тестами.
macros/Reusable Jinja-макросы. Эквивалент функций.
analyses/Ad-hoc SQL для анализа. Компилируется, но не выполняется.
docs/Markdown-файлы с doc-blocks для документации.
генерируется автоматически
target/Compiled SQL, manifest.json, catalog.json, run_results.json. Регенерируется на dbt run.
dbt_packages/Установленные пакеты после dbt deps. Эквивалент node_modules.
logs/dbt.log с детальным логом запусков.

Звёздочка — то, с чем работаете каждый день. (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 получает:

  1. Project-level +materialized: view (default)
  2. Перебивает marts.+materialized: table
  3. И уже это перекрывает {{ 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.
TIP

Подчёркивание _ в имени 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).

WARNING

Не кладите большие 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-проект, но не должны:

  1. Sample CSV для тестирования — кладите в seeds/, но только если они малы (меньше 5MB). Большие — в отдельный data layer.
  2. Скрипты для загрузки данных в warehouse (Python для Stripe API) — это E+L, отдельный инструмент (Fivetran/Airbyte/custom Airflow DAG). Не место в dbt.
  3. Скрипты для отправки emails / Slack — это оркестрация, в Airflow или dbt Cloud Jobs.
  4. Tableau workbooks / Looker LookML — в своих репозиториях BI-инструментов.
  5. Документация по бизнес-процессам компании — в Confluence/Notion. В dbt — только техническая документация про модели.

Попробуй сам

Возьмите свой dbt-проект из предыдущего урока:

  1. Откройте dbt_project.yml. Найдите секцию models:. Удалите example: (мы её уже убрали).
  2. Добавьте структуру:
    models:
      jaffle_shop:
        +materialized: view
        staging:
          +materialized: view
        intermediate:
          +materialized: ephemeral
        marts:
          +materialized: table
  3. Создайте папки: mkdir -p models/staging models/intermediate models/marts
  4. Запустите dbt parse — это валидация YAML без выполнения. Должно пройти без ошибок.
  5. Запустите dbt list --select staging — пустой список (моделей в staging пока нет, но команда не падает).
  6. Посмотрите target/manifest.json: cat target/manifest.json | head -50. Большой JSON с описанием каждой ноды графа. На этом этапе моделей нет, но структура уже видна.

После этого ваш проект — production-ready skeleton, готовый принять реальные модели.


git init: устройство репозитория
Проверка знанийKnowledge check
В чём принципиальная разница между файлами target/compiled/<model>.sql и target/run/<model>.sql, и зачем dbt хранит оба?
ОтветAnswer
target/compiled/<model>.sql — это чистый SQL модели после рендеринга Jinja: все ref() заменены на полные имена таблиц (database.schema.table), все макросы развёрнуты, var() и env_var() подставлены. Этот SQL можно скопировать и выполнить в warehouse SQL editor — он вернёт результаты модели. target/run/<model>.sql — это тот же compiled SQL, но обёрнутый в DDL: CREATE TABLE / CREATE VIEW / merge logic для incremental. То есть target/run — это то, что dbt реально отправил в warehouse при dbt run. dbt хранит оба для разных целей: compiled полезен для debug-сессий ("что именно я написал, после раскрытия Jinja"), для копирования в ad-hoc запросы, для понимания компиляции макросов; run полезен для понимания "что именно warehouse увидел" — особенно важно для incremental моделей с MERGE-логикой, где warehouse получает совсем другой SQL чем тот, что в .sql модели. Эта разница — основа debug workflow в модуле 18.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 6. В dbt_project.yml есть секция `models:` с конфигом для подпапок staging, intermediate, marts. Что определяет конфиг `marts: +materialized: table`?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4