dbt Mesh: введение
dbt Mesh — паттерн governance для больших dbt-проектов, который позволяет разделить один монолитный проект на несколько связанных проектов (per-domain) с контролируемым cross-project ref. Архитектурно это аналог микросервисов для данных: вместо одного монорепо — несколько проектов с явными контрактами.
Полные senior-детали (внутреннее устройство Mesh, performance-tuning, инструменты cross-project lineage) разбираются в dbt III. В этом уроке — концептуальное введение и patterns, которые middle должен понимать.
Проблема, которую решает Mesh
dbt-iii: Mesh архитектура — детальный разбор Data Domains и владение — governance-основание MeshБез Mesh большие data-команды живут в монорепо: один dbt-проект, 500+ моделей, все ref’ы внутри. Через год-два роста начинаются проблемы:
- Парсинг и компиляция медленные. На 1000+ моделей
dbt parseзанимает минуту, CI работает 30 минут на любой PR. - Конфликты ownership. Marketing team меняет модель, которой пользуется Finance team. Без явных контрактов — постоянные регрессии.
- Большой blast radius. Изменение в
stg_usersзатрагивает 300 downstream моделей. Никто не уверен, что не сломает. - Onboarding cognitive overload. Новый dev открывает проект с 500 моделями в одной папке — теряется.
- CI bottleneck. Каждый PR валидирует весь проект, даже если изменение в одной domain-области.
Mesh — это архитектурное решение: разделить монолит на несколько проектов с контрактами. Аналогично transition’у с monolith application на microservices.
Архитектура Mesh
Каждый проект — отдельный git-repo (обычно) с собственным dbt_project.yml. Связь между проектами — через cross-project ref:
-- В dbt_marketing/models/marts/mart_campaign_revenue.sql
SELECT
c.campaign_id,
c.campaign_name,
d.fiscal_quarter
FROM {{ ref('campaigns_attributed') }} AS c
JOIN {{ ref('dbt_platform', 'dim_date') }} AS d
ON c.day = d.date_day
{{ ref('dbt_platform', 'dim_date') }} — это cross-project ref, который резолвится в production-схему dbt_platform.
Model groups
Model group — это namespace внутри одного проекта, который группирует модели по domain и устанавливает им общий owner и default access.
Декларация группы в _models.yml:
groups:
- name: marketing_core
owner:
name: Marketing Analytics Team
email: [email protected]
- name: marketing_internal
owner:
name: Marketing Analytics Team
email: [email protected]
Привязка модели к группе:
models:
- name: stg_marketing_campaigns
group: marketing_internal
access: private
description: "..."
- name: mart_campaigns_summary
group: marketing_core
access: public
description: "..."
Зачем groups внутри одного проекта (не только cross-project)?
- Ownership clarity. Per-model owner превращается в per-group, реже меняется.
- Default access. На уровне группы можно установить default access (private/protected/public) для всех моделей группы.
- Foundation для split на проекты. Если у вас в монорепо хорошо организованы groups — split в Mesh-проекты делается легко: вы уже знаете границы domain.
Access levels
access — это аннотация на модели, определяющая, кто может ref’ить эту модель:
| Access | Кто может ref | Use case |
|---|---|---|
private | Только модели той же группы внутри того же проекта | Internal staging, helper, intermediate |
protected | Любая модель того же проекта (default) | Любая внутренняя модель проекта |
public | Любая модель из любого проекта | Domain-level marts, public dimensions |
models:
# stg_marketing_campaigns — internal staging, никто кроме marketing не должен ref'ить
- name: stg_marketing_campaigns
group: marketing_internal
access: private
# mart_campaigns_summary — domain-mart, exposed для cross-project
- name: mart_campaigns_summary
group: marketing_core
access: public
Что enforced и что нет
Важно: access — это enforcement-flag, который dbt проверяет при dbt parse. Если другой проект попытается {{ ref('dbt_marketing', 'stg_marketing_campaigns') }} (где модель private), dbt выдаст ошибку.
Это не просто метаdata — это активный механизм governance.
access не защищает от прямого SQL-доступа на warehouse-уровне. Если в Snowflake таблица создаётся с правами на всех, любой пользователь может SELECT * FROM marketing.stg_marketing_campaigns вне dbt. access работает только в dbt-context’е. Реальные warehouse-level grants управляются через grants config (отдельный модуль 11 multi-environment).
Cross-project ref: механика
Когда вы пишете {{ ref('dbt_platform', 'dim_date') }} в проекте dbt_marketing, dbt должен знать:
- Какой проект
dbt_platform? Где его конфиг? - В какой схеме живёт
dim_dateв production? - Версия модели — есть ли deprecation_date, какая latest_version?
Эта информация передаётся через dependencies.yml в потребляющем проекте:
# dbt_marketing/dependencies.yml
projects:
- name: dbt_platform
И через manifest.json от dbt_platform, который Cloud / Studio автоматически делает доступным consuming проектам.
В Core это требует ручного выкладывания manifest.json от dbt_platform в S3, а consuming-проект скачивает его перед dbt parse. В Cloud Mesh это работает “из коробки” — Cloud хранит manifest всех projects и автоматически разрешает cross-project ref.
Governance patterns
Mesh даёт инструменты, но политика — это правила команды.
Public API contracts
Public-модели — это API проекта для других проектов. К ним должны применяться более строгие правила:
- Model contracts включены: enforced columns + types + constraints
- Versions: model versions с
latest_versionиdeprecation_date - Tests: уровень тестов выше, чем для internal
- Documentation: полное описание columns, business meaning
Внутренние модели (private) могут быть менее формальными — это implementation detail группы.
Deprecation workflow
Когда public-модель меняет API (добавили / удалили / переименовали колонку):
- Создать новую версию:
mart_campaigns_summaryv2 вmart_campaigns_summary_v2.sql - Обновить yml:
latest_version: 2,versions: [v: 1, deprecation_date: '2026-09-01'], [v: 2] - Уведомить downstream consumers (через Slack, ownership-routing)
- Дать window 1-3 месяца на миграцию
- После deprecation_date удалить v1
Это аналог API versioning в HTTP-сервисах. Mesh без deprecation_workflow быстро ломается.
Ownership и responsibility
Каждая public-модель имеет явного owner (через group). Owner отвечает за:
- Поддержку модели (тесты зелёные, freshness ок)
- Communication с downstream consumers
- Deprecation announcements
- On-call для incidents related к модели
Без явного ownership Mesh не работает — public-модели становятся “ничейными”.
Когда переходить на Mesh
Mesh — серьёзная архитектурная decision. Не делайте этого преждевременно.
Признаки, что Mesh нужен
- Проект >500 моделей. На меньшем размере overhead Mesh (cross-project complexity, deployment coordination) не окупается.
- 3+ data-команды в компании, каждая со своим domain. Если одна команда — Mesh искусственный.
- Cross-team conflicts постоянны. Один dev сломал что-то, что важно другой команде, регрессии часто.
- CI занимает >20 минут на медианный PR. Split на проекты ускоряет CI значительно.
- Onboarding занимает >1 месяц. Слишком много структуры для нового dev’а.
Признаки, что Mesh преждевременен
- Менее 200 моделей в проекте. Просто наладьте папки
staging/intermediate/martsи groups. - 1-2 dev’а на проект. Mesh-overhead больше, чем benefit.
- Mono-domain analytics. Если все ваши analytics — про один бизнес-процесс, нет естественных domain-границ.
- CI быстрый. Если Slim CI на state:modified+ уже даёт CI меньше 10 минут — переход на Mesh не даст значительного speedup.
Промежуточный этап: groups внутри монорепо
Хороший foundation перед Mesh — это groups + access внутри одного проекта. Вы декларируете groups, прописываете access (private/protected/public), и устанавливаете governance-правила, но физически проект остаётся одним. Когда боль роста перерастает groups — split в Mesh готов на 80%, потому что границы уже определены.
Cross-project ref: технические нюансы (introductory)
Что middle должен знать
- Cross-project ref работает только для public моделей.
- Resolves в production-схему target проекта, не в локальную dev-схему.
- Defer работает через cross-project: можно работать с upstream
dbt_platformв dev, ref’ить prod-таблицы downstream проекта. - Lineage в Explorer показывает связи между проектами. Можно кликом перейти из marketing-модели в platform-upstream.
Что senior должен знать (dbt III)
- Performance: cross-project parse может быть медленным, если consumer проект ref’ит много public моделей.
- State management: как координировать deploy между проектами без race-condition.
- Versioning conflicts: что делать, если consumer ref’ит v1, а producer уже на v2.
- Local development: как запустить
dbt runлокально, если ваш проект ref’ит cross-project, а вы не хотите подключаться к prod-warehouse.
Mesh в Core vs Cloud
Mesh частично работает в Core, полностью — в Cloud.
| Feature | Core | Cloud Enterprise |
|---|---|---|
| Model groups | Да, в _models.yml | Да |
| Access levels (private/protected/public) | Да, enforced при parse | Да |
| Cross-project ref | Да, через dependencies.yml + manual manifest distribution | Да, automatic manifest discovery |
| Cross-project lineage в UI | Только локальный dbt docs | Full в Explorer |
| Cross-project access policies | Только через manifest validation | + UI governance |
| Cross-project performance optimizations | Manual | Optimized internally |
| Cross-project version policies | Manual через deprecation_date | UI deprecation tracking |
Если у вас Core — Mesh реализуем, но требует существенной инженерной работы для production-grade setup. В Cloud Enterprise это работает “из коробки”.
Что middle-инженер должен уметь
- Объяснить проблему, которую решает Mesh, и когда он преждевременен (size + team count + CI bottleneck).
- Определить model groups в
_models.ymlс owner. - Прописать access (private/protected/public) на моделях, понимая, что enforced.
- Знать синтаксис cross-project ref:
{{ ref('project_name', 'model_name') }}. - Назвать elements governance pattern: public API contracts, deprecation workflow, ownership.
- Аргументировать, готова ли ваша команда к Mesh.
Попробуй сам
В вашем текущем dbt-проекте (если он >100 моделей):
- Identify groups. Сгруппируйте модели по domain (marketing / finance / product / platform). Сколько groups получается?
- Identify owners. Кто owner каждой group? Если “никто” — это знак, что Mesh преждевременен (или organization issue).
- Identify access. Какие модели должны быть public (используются за пределами своей группы)? Какие private?
- Map cross-group dependencies. Какая группа ref’ит какую? Является ли граф ациклическим?
Это упражнение — pre-work для Mesh, даже если вы не планируете split. Просто наличие этой структуры улучшает governance.
Откройте https://docs.getdbt.com/docs/mesh/about-mesh — официальная документация Mesh. Изучите концепции model_groups, access, cross-project ref. Senior-детали (versioning, performance, governance patterns) — в dbt III.