Capstone: project overview
Welcome to the capstone. Это финальный проект курса dbt II, который связывает все 15 предыдущих модулей в одно production-grade приложение. Объём — 12-15 часов вашего времени, в результате — портфолио-проект, который можно показывать на интервью.
В этом уроке — обзор: что строим, зачем именно так, какие требования, как будем проверять “готово”.
dbt-iii: что дальше после этого capstoneЧто мы строим
E-commerce analytics pipeline для гипотетического интернет-магазина “GrandShop”. Это типичный workload, который вы встретите в любой product analytics команде. Бизнес-контекст:
- Магазин продаёт ~50k SKU
- ~10k orders в день, growing
- 5 sources: orders DB (Postgres), events stream (Kafka -> S3 -> DuckDB), product catalog (Postgres), users DB (Postgres), marketing events (CSV exports)
- Stakeholders: CEO/CFO (revenue dashboards), product team (conversion analytics), marketing (campaign attribution), customer support (order lookup)
Что мы построим: 30+ dbt-моделей в medallion-структуре, semantic layer для 3 ключевых метрик, CI/CD с Slim CI, документация, exposures для downstream-систем.
Технологический стек
| Component | Choice | Обоснование |
|---|---|---|
| Warehouse | DuckDB (через dbt-duckdb 1.10) | Локально, без cloud, без credentials. Производственно использовался бы Snowflake / BigQuery — но архитектурные паттерны те же. |
| dbt | dbt-core 1.10 | Текущий stable. Все фичи курса работают: microbatch, snapshots с hard_deletes, semantic_models, model contracts. |
| CI/CD | GitHub Actions | Открытый, бесплатный, де-факто стандарт. Slim CI через state:modified+ + defer. |
| Pre-commit | sqlfluff + dbt-checkpoint | Local quality gate перед PR. |
| Packages | dbt_utils, dbt_expectations, codegen | Стандартный набор production-проекта. |
| Data orchestration | None для DuckDB capstone | В реальности это были бы Airflow / Dagster / Cloud Jobs. |
DuckDB caveats для capstone. dbt-duckdb 1.10 имеет ограничения относительно Snowflake / BigQuery:
- Microbatch incremental — частично поддержан, на простых случаях работает
- Snapshot hard_deletes invalidate_hard_deletes — workaround через post-hook
- Materialized views — не поддерживаются
- Iceberg — не нативно, через extensions
В реальности на production вы бы использовали Snowflake (полный feature parity) или BigQuery. Но архитектурно проект одинаков. Где DuckDB не справляется — мы честно скажем “в prod-warehouse это работало бы так, у нас workaround или пропускаем”.
Архитектура pipeline: medallion + marts
Итого: 8 staging + 10 intermediate + 8 marts + 3 snapshots = 29 моделей. Плюс tests, semantic models, exposures, docs.
Production-ready чек-лист
Это критерии, по которым проверяем “готово”. Каждый пункт — обязательный.
Архитектура и структура
- Папочная структура:
models/staging/{source}/,models/intermediate/{domain}/,models/marts/{domain}/ - Naming convention:
stg_<source>__<entity>,int_<domain>__<purpose>,<grain>_<entity>для marts - Каждая папка имеет
_models.ymlи_sources.yml(где применимо) -
dbt_project.ymlвалиден, profile настроен на DuckDB - packages.yml с dbt_utils, dbt_expectations
Sources и freshness
- Все 5 sources задекларированы в
_sources.yml - Freshness rules на критичных sources (orders, events)
- Source-уровневые тесты: not_null на primary keys
Models: materializations
- Staging:
materialized='view'(default для staging) - Intermediate:
materialized='ephemeral'илиview(по обстоятельствам) - Marts:
materialized='table'для большинства,incrementalдля большой fact (orders) -
mart_revenue_daily— incremental сmergestrategy -
mart_orders— incremental сinsert_overwritestrategy (или microbatch на простых случаях)
Snapshots
-
customers_snapshotс timestamp strategy -
products_snapshotс check strategy -
prices_snapshotс timestamp + invalidate_hard_deletes (или workaround на DuckDB) -
dbt_valid_to_currentconfigured
Tests
- Стандартные тесты: not_null, unique, accepted_values, relationships на ключевых колонках всех marts
- dbt-expectations: 5+ продвинутых тестов (expect_column_values_to_be_between, expect_table_row_count_to_be_between)
- Custom generic test: 1+ (например,
test_revenue_consistency) - Unit tests: 3+ unit tests на критичные модели (например,
int_marketing_attribution) - Severity:
errorдля critical,warnдля quality checks -
store_failures: trueна 3+ ключевых тестах для debugging
Model contracts и versions
- 3+ marts с enforced contracts (mart_revenue_daily, mart_users_360, mart_orders)
- 1 mart с versions:
mart_revenue_dailyимеет v1 и v2
Documentation
- Все marts описаны в yml: description + column descriptions
- Используется doc block для повторяющихся описаний
-
dbt docs generateработает без ошибок -
persist_docs: { relation: true, columns: true }для marts
Semantic Layer
- 3 semantic_models:
revenue— simple metric (SUM revenue)conversion_rate— ratio metric (orders / sessions)cumulative_revenue— cumulative metric (running total)
- Saved queries для типичных BI views
Exposures
- 5 exposures: executive_dashboard, marketing_attribution_report, ml_churn_model, customer_support_lookup, finance_close_pack
- У каждого exposure указан owner
CI/CD
- GitHub Actions workflow в
.github/workflows/dbt-ci.yml - Workflow запускается на pull_request
- Slim CI:
dbt build --select state:modified+ --defer --state ./prod_state - Prod state хранится как GitHub artifact или в S3 (мы используем artifact)
- Source freshness check в CI
- Pre-commit hooks: sqlfluff + dbt-checkpoint в
.pre-commit-config.yaml
Quality
-
dbt build(полный) зелёный: все models успешно построены, все tests passed -
dbt compileбез warnings - sqlfluff lint passing на всём проекте
- README.md с описанием проекта и how-to-run
Структура проекта в репозитории
dbt-capstone-ecommerce/
├── README.md
├── dbt_project.yml
├── packages.yml
├── profiles.yml.example
├── .pre-commit-config.yaml
├── .github/
│ └── workflows/
│ └── dbt-ci.yml
├── seeds/
│ ├── raw_users.csv
│ ├── raw_orders.csv
│ ├── raw_order_items.csv
│ ├── raw_products.csv
│ ├── raw_events.csv
│ └── raw_marketing_events.csv
├── models/
│ ├── staging/
│ │ ├── orders_db/
│ │ │ ├── _sources.yml
│ │ │ ├── _models.yml
│ │ │ ├── stg_orders_db__orders.sql
│ │ │ ├── stg_orders_db__order_items.sql
│ │ │ ├── stg_orders_db__payments.sql
│ │ │ └── stg_orders_db__addresses.sql
│ │ ├── events/
│ │ │ ├── _sources.yml
│ │ │ ├── _models.yml
│ │ │ └── stg_events__page_views.sql
│ │ ├── products_db/
│ │ ├── users_db/
│ │ └── marketing/
│ ├── intermediate/
│ │ ├── orders/
│ │ ├── users/
│ │ ├── marketing/
│ │ └── events/
│ ├── marts/
│ │ ├── core/
│ │ │ ├── _models.yml
│ │ │ ├── mart_orders.sql
│ │ │ ├── mart_revenue_daily.sql
│ │ │ ├── mart_revenue_daily_v2.sql
│ │ │ └── mart_users_360.sql
│ │ ├── marketing/
│ │ ├── product/
│ │ └── finance/
│ └── _exposures.yml
├── snapshots/
│ ├── customers_snapshot.yml
│ ├── products_snapshot.sql
│ └── prices_snapshot.yml
├── tests/
│ ├── generic/
│ │ └── test_revenue_consistency.sql
│ └── singular/
│ └── orders_total_matches_items.sql
├── unit_tests/
│ ├── int_marketing_attribution_unit.yml
│ ├── mart_orders_unit.yml
│ └── mart_revenue_daily_unit.yml
├── macros/
│ ├── generate_schema_name.sql
│ ├── get_date_dim.sql
│ └── custom_test_macros.sql
└── semantic_models/
├── revenue.yml
├── conversion_rate.yml
└── cumulative_revenue.yml
Это полная структура. Можно начинать с пустого репо и заполнять по шагам в следующих уроках.
Тайминг работы
Capstone не делается за вечер. Реалистичный план — 5 сессий по 2-3 часа:
| Сессия | Объём работы | Фокус |
|---|---|---|
| 1 (3 часа) | Setup + sources + 8 staging моделей | dbt-duckdb настроен, seeds загружены, staging работает |
| 2 (3 часа) | 10 intermediate + 5 marts (without contracts) | Базовый DAG зелёный, все ref’ы работают |
| 3 (2.5 часа) | Incremental + snapshots + 3 contracts | Production-grade marts (mart_orders incremental, snapshots SCD2) |
| 4 (2.5 часа) | Tests (data + unit) + dbt-expectations + custom generic | Полное покрытие тестами |
| 5 (3 часа) | Semantic Layer + exposures + CI + docs + README | Финал: CI зелёный, semantic_models работают |
Итого ~14 часов. Реалистично растянуть на 2 недели с темпом 2 вечера + 1 weekend.
Эволюция от course-проекта Jaffle Shop
Jaffle Shop — учебный dbt-проект из официальной документации. Capstone — это его production-эволюция:
| Jaffle Shop (tutorial) | Capstone (production-grade) |
|---|---|
| 5 моделей | 29 моделей |
| Только staging + marts | staging + intermediate + marts + snapshots |
| Только not_null/unique tests | + dbt-expectations + unit tests + custom generic |
| Materialization = view + table | + incremental + ephemeral + snapshots |
| Нет documentation | + persist_docs + doc blocks |
| Нет CI | + GitHub Actions Slim CI + pre-commit |
| Нет semantic layer | + 3 semantic_models |
| Нет contracts | + 3 enforced contracts |
| Нет exposures | + 5 exposures |
Capstone закрывает разрыв между туториалом и реальным продакшном.
Что middle-инженер должен унести
После capstone у вас в портфолио будет:
- Полный production-grade dbt-проект на 30+ моделях
- Опыт всех ключевых фич dbt 1.10+: incremental, microbatch, snapshots, unit tests, contracts, versions, semantic_models
- Рабочий CI/CD pipeline на GitHub Actions
- Документация и exposures
- Понимание trade-offs DuckDB vs Snowflake/BigQuery
На интервью это даёт proof of work. Вместо “я учил dbt” — “у меня в репо production-grade проект, могу показать”.
Попробуй сам (setup для следующих уроков)
Прежде чем переходить к уроку 2, подготовьте окружение:
- Создайте новый git-репозиторий
dbt-capstone-ecommerce. - Установите dbt-duckdb:
python -m venv venv source venv/bin/activate pip install dbt-duckdb==1.10.0 - Инициализируйте dbt-проект:
dbt init dbt_capstone_ecommerce # выберите duckdb adapter - Настройте profiles.yml на DuckDB:
dbt_capstone_ecommerce: outputs: dev: type: duckdb path: 'capstone.duckdb' threads: 4 prod: type: duckdb path: 'capstone_prod.duckdb' threads: 4 target: dev - Проверьте:
dbt debugдолжен показать OK.
В уроке 2 начнём грузить seeds и писать staging-модели.