dbt Multi-Project Architecture in BigQuery: Split It Right
Running a single dbt project works — until it doesn't. Learn when to split into multiple projects in BigQuery, how to do it without breaking lineage, and the hidden costs teams miss.
When Should You Split Your dbt Project in BigQuery?
You should split a dbt monorepo into multiple projects in BigQuery when a single project's DAG has grown beyond ~300 models, ownership boundaries are unclear, or different teams need independent deploy cadences. Done correctly, splitting unlocks faster CI, cleaner domain ownership, and better cost attribution. Done incorrectly, it severs lineage, breaks ref() resolution, and introduces a class of silent query cost problems that are very hard to diagnose after the fact.
This is one of the most consequential architectural decisions a data team makes as it scales — and in our experience, most teams make it six months too late and in the wrong way. Here is what the right approach actually looks like.
Why the dbt Monorepo Breaks Down at Scale
Every team starts with a single dbt project. It is the right call at the beginning: one repo, one DAG, one deploy, full lineage in a single view. At ten or twenty models, it is genuinely delightful. At a hundred models, it is still manageable. At two hundred and fifty models across four product domains, with six engineers committing to the same repo and three different stakeholders demanding different deploy frequencies, the wheels start to come off.
A pattern we see repeatedly in our work with Series A and Series B companies: the data team has quietly become a bottleneck. A finance analyst needs a model change deployed, but the CI pipeline is blocked by a broken test three domains away. A product engineer wants to add a new mart but cannot get a review slot. The staging layer is a tangle of models from three different teams with no clear ownership, and nobody is sure which models are safe to change.
The monorepo is no longer a shared asset — it is a shared liability.
At this point, splitting into multiple dbt projects is the right architectural move. But most teams underestimate what it actually involves.

📺 Watch: Episode 3: Creating your first dbt™ project | Building your modern data stack
What Actually Breaks When You Split a dbt Project the Wrong Way
Here is where teams get into serious trouble. They decide to split. They move a set of models into a new repo. They point the new project at the same BigQuery datasets. And then they run into a wall.
Broken ref() resolution. In dbt, ref() is how the DAG is built — it tells dbt about dependencies between models and ensures they run in the right order. When you move models into a separate project, any downstream model in the original project that referenced those models via ref() now has a broken dependency. The options at this point are either to use hardcoded table names (which destroys lineage — a problem we have written about in detail here), or to reach for dbt Mesh — which requires dbt Enterprise and a proper understanding of public model contracts.
Invisible query cost duplication. This is the one that bites hardest. When two separate dbt projects both reference the same upstream BigQuery table without a shared materialised intermediate, you can end up with both projects scanning the full source table independently. We have seen early-stage companies double their BigQuery scanning costs in the weeks after a project split — not because they added new models, but because the shared intermediate that was absorbing the cost in the monorepo no longer exists. A single SELECT * on a 10TB table costs $62.50 at on-demand rates — and if two projects are both doing it on the same run schedule, you are paying twice for work that was previously done once.
Lineage that stops at the project boundary. When your downstream mart in Project B references a model from Project A via a hardcoded table name rather than a proper cross-project ref(), the lineage graph is broken. You lose impact analysis. You lose the ability to trace a metric back to its source in a single view. When a source schema changes — and it always does — you have no automated way to know which downstream models in which projects are affected. This is the dbt source fan-out problem made significantly worse, because now it spans project boundaries.
Testing gaps at the seam. In a monorepo, you can run dbt test across the entire DAG in one command. In a multi-project setup, each project's test suite only covers its own models. The models that sit at the boundary — consuming outputs from one project and feeding inputs to another — are precisely where data quality issues are most likely to surface, and they are also the models least likely to be covered by tests in either project's test suite.
The Right Way to Split: A Decision Framework
Before you touch a single file, answer these four questions:
1. Do you have genuine domain boundaries? The right reason to split a dbt project is because you have genuinely distinct data domains with different owners, different deploy cadences, and different consumers. Finance models owned by the finance analytics team, product models owned by the product data team, and platform models owned by the data platform team is a legitimate split. Splitting by technical layer — staging in one project, marts in another — is almost always the wrong call. It creates artificial coupling with no ownership benefit.
2. What are your cross-domain dependencies? Draw your DAG. Identify every edge that crosses a proposed domain boundary. Each of those edges is a cross-project reference you will need to manage. If the number of cross-domain edges is high relative to the total edge count, your proposed split is fighting the natural grain of your data. The models are coupled for a reason — rethink the boundary.
3. How will you handle cross-project refs? If you are on dbt Cloud Enterprise, use dbt Mesh with public model contracts. dbt Mesh treats cross-project dependencies as a metadata-resolved API — dbt resolves references on-the-fly to public models defined in other projects, so you do not need to parse or run upstream models yourself. If you are on dbt Core or dbt Cloud Developer/Team, you will need to think carefully about how downstream projects consume upstream outputs — materialised tables consumed as source() references with freshness checks is the pragmatic fallback, but you must document this explicitly and accept the lineage gap.
4. What is your materialisation strategy at the boundary? Whatever models sit at the project boundary — the outputs that other projects consume — should be materialised as tables, not views. A view at a project boundary means every downstream query in the consuming project scans through the view back to the underlying source data. At BigQuery's on-demand pricing of $6.25 per TB scanned, that adds up fast. Materialise your boundary models as tables, partition them correctly, and treat them as a contract.
If you are looking to design this kind of architecture for your organisation, explore how Fintel Analytics approaches data engineering and dbt project design — we work with growth-stage businesses globally to build dbt projects that are structured to scale without breaking.

How to Execute the Split Without Breaking Production
Once you have answered those four questions, here is the sequence that works in practice:
Step 1 — Identify and materialise boundary models before the split. In the monorepo, find every model that will become a cross-project dependency. Change their materialisation to table and add appropriate partitioning and clustering. Run a full dbt build and confirm the output tables are clean and tested. These tables will become the "published API" of your upstream project.
Step 2 — Create the downstream project against existing tables. Stand up the new dbt project and configure sources pointing to the materialised boundary tables. Do not move any models yet. Run dbt compile and confirm the new project resolves correctly. This is your safety net — the monorepo still owns everything, and the new project is just reading from it.
Step 3 — Migrate models incrementally, domain by domain. Move models into the new project in topological order — sources first, then staging, then intermediate, then marts. After each batch, run both projects and diff the outputs against the monorepo outputs. A reconciliation check at each step is not optional; it is how you catch the silent issues before they reach a dashboard.
Step 4 — Establish cross-project test coverage at the boundary. The models that the consuming project reads from the producing project need freshness checks in sources.yml and at least row-count and null-check tests on key columns. This is your early-warning system for the integration point that neither project's standard test suite covers automatically.
Step 5 — Deprecate and clean up the monorepo. Only once the new project has been running cleanly in production for at least two full deploy cycles should you remove the migrated models from the monorepo. Keep the boundary table materialisations in place — they are now owned by the upstream project.
In one engagement with a Series B fintech, we executed this migration across a project with just over 200 models in three domains. The monorepo had grown to the point where a full dbt build was taking over 45 minutes and blocking two separate teams from deploying independently. After the split — executed over six weeks with zero production incidents — each domain project ran a full build in under eight minutes, and the finance team were deploying independently three times a week instead of waiting for a weekly joint release window.
What Does This Cost, and What Does It Save?
The cost conversation is the one that gets skipped most often in architectural decisions — because engineers are focused on correctness, not bills. But the materialisation choices made during a project split have direct and immediate cost consequences.
A poorly executed split where boundary models are left as views, and both projects scan the same source tables independently, can double query costs overnight. Partitioning and clustering reduce query costs by 60–90% by limiting data scanned — and if your boundary tables are being created without partition and cluster configs, you are giving that saving back immediately.
Conversely, a well-executed split with correctly materialised, partitioned boundary tables and clear ownership of query costs per project gives you something valuable that the monorepo never could: per-domain cost attribution. You can now answer the question "how much does running the finance domain cost per day" — which is the first step toward meaningful BigQuery cost governance. A single unoptimised SELECT * query has cost teams $62.50, and twenty analysts running similar queries can waste $5K to $10K a month. Domain-level cost visibility is what makes that kind of spend visible before it compounds.
The investment in a properly structured multi-project architecture typically pays back in reduced CI time, reduced query costs, and reduced engineering coordination overhead within one quarter. The teams we have seen resist the split — trying to scale a monorepo past 400 models — end up spending significantly more in engineering time managing the complexity than they would have spent on the migration.
Frequently Asked Questions
Q: When is the right time to split a dbt project into multiple projects?
A: The right signal is when ownership, deploy cadence, or domain boundaries have genuinely diverged — typically around 200–300 models across 3 or more distinct business domains, or when a single broken test in one domain is blocking deploys for another team. Splitting earlier than this usually creates unnecessary coordination overhead without meaningful benefit.
Q: Can I use dbt Mesh for cross-project refs in BigQuery without dbt Enterprise?
A: dbt Mesh with first-class cross-project ref() resolution and public model contracts requires dbt Enterprise. On dbt Core or dbt Cloud Team, the pragmatic alternative is to materialise upstream boundary models as tables and reference them as source() in the downstream project, with freshness checks configured. You lose automatic lineage across the boundary, but you retain correctness and can manage the gap manually.
Q: Will splitting my dbt project into multiple projects increase my BigQuery costs?
A: It can — if boundary models are left as views instead of being materialised as tables, both projects may scan the same upstream data independently, doubling scanning costs. The fix is to materialise boundary models as partitioned, clustered tables before the split, treating them as published outputs rather than pass-through views.
Q: How do I maintain lineage visibility across multiple dbt projects in BigQuery?
A: On dbt Enterprise, dbt Mesh surfaces cross-project lineage in dbt Cloud's DAG view automatically. On dbt Core, you need an external lineage tool (such as DataHub or Atlan) that can stitch together the lineage graphs from multiple projects, or you need to maintain explicit documentation of cross-project dependencies as part of your data contract process.
Q: What materialisation should boundary models use in a multi-project dbt setup?
A: Always table, with appropriate partitioning by date or timestamp and clustering by the most commonly filtered columns. Never view at a project boundary — a view at the seam means every downstream query in the consuming project scans through it back to the source data, compounding costs and removing the caching benefits that a materialised table provides.
Splitting a dbt monorepo into multiple projects in BigQuery is one of those decisions that looks straightforward on paper and reveals its complexity only once you are in the middle of it — broken refs, invisible cost duplication, and lineage gaps that do not show up until something downstream breaks in production. At Fintel Analytics, we have guided multiple growth-stage companies through exactly this transition, from the initial domain boundary design through to production deployment and cost validation — and we know where the hidden traps are. If your data team is feeling the friction of a monorepo that has outgrown its original design, that is a solvable problem with a clear path forward.
