Multi-project deployments enable organizations to scale dbt across teams and domains while maintaining code ownership and access control. This architecture is essential for large organizations with multiple data teams.
When to Use Multi-Project
Reason
Benefit
Team autonomy
Different teams own different data domains
Access control
Restrict visibility of sensitive models
Deployment independence
Deploy projects on different schedules
Code organization
Separate concerns by business domain
Performance
Reduce build scope for individual teams
Project Roles
Role
Description
Example
Hub
Central shared models
Core analytics, dimensions
Domain
Team-specific models
Marketing, Finance, Product
Downstream
Consumer models
BI tools, ML pipelines
Shared
Cross-cutting concerns
Logging, auditing
Key Takeaway: Start with a single project. Extract shared models into a hub when you have 3+ teams or need access control. Over-engineering early adds unnecessary complexity.
-- hub_project/models/marts/dim_customers.sql
{{
config(
materialized='table',
access='public',
tags=['public', 'dimensions']
)
}}
with customers as (
select * from {{ source('erp', 'customers') }}
),
final as (
select
customer_id,
customer_name,
email,
segment,
created_at,
updated_at
from customers
)
select * from final
-- marketing_project/models/fct_campaign_performance.sql
{{
config(
materialized='incremental',
unique_key='campaign_id'
)
}}
with campaigns as (
select * from {{ source('marketing', 'campaigns') }}
),
-- Cross-project reference to hub project
customers as (
select * from {{ ref('hub_analytics', 'dim_customers') }}
),
campaign_metrics as (
select
c.campaign_id,
c.campaign_name,
c.channel,
c.budget,
c.spend,
cust.segment as customer_segment,
count(distinct cust.customer_id) as unique_customers,
sum(c.spend) as total_spend
from campaigns c
left join customers cust on c.customer_id = cust.customer_id
group by 1, 2, 3, 4, 5, 6
)
select * from campaign_metrics
-- models/internal/int_revenue_calculation.sql
{{
config(
materialized='ephemeral',
access='private'
)
}}
{#- This model is private and cannot be referenced by external projects -#}
{#- It contains proprietary business logic -#}
with orders as (
select * from {{ ref('stg_orders') }}
),
revenue_calc as (
select
order_id,
sum(amount * 0.85) as adjusted_revenue,
sum(amount * 0.15) as platform_fee
from orders
group by 1
)
select * from revenue_calc