CertSafari
    DBT Architect· Lessons

    Domain 7 · Lesson 35/39

    Adding dbt projects to a mesh

    Setting up additional dbt projects

    16 min read
    2.56% of exam
    5 sources
    Published 13 Oct 2026
    Docs as of 12 Oct 2026

    What you will be able to do

    • Decide where to split a dbt DAG into separate projects and whether to use one repository or several
    • Create an additional dbt project with its own connection, repository and deployment environment
    • Move a group of models out of a monolith into a new project, or migrate from package- or source-based coordination
    • Declare a project dependency in dependencies.yml and reference upstream public models with a two-argument ref
    • Set up two projects that depend on each other in the right order

    Key concept

    Project dependency — A project dependency is an entry in dependencies.yml that lets one dbt project ref another project's public models without importing its code. A metadata service resolves each reference against what the upstream project has already built, so the downstream project never parses or runs the upstream models.

    1.When a second project is worth it, and where to cut

    Add a second dbt project when a single project is causing problems for the organisation, not as a starting setup. The usual signs are these. The model count is slowing development. Teams' workflows have drifted apart and need to be separated. Data products are becoming less reliable because teams struggle to communicate. Security or governance requirements would benefit from isolation. A team just starting with dbt does not need any of this yet. The features behind a multi-project setup work on their own and can be adopted as the project grows.

    Cross-project references are the feature that turns several projects into one mesh. They are available only on the dbt Enterprise and Enterprise+ plans. On any lower plan you can still have separate projects, but a ref cannot reach from one to another.

    The hardest decision is where to draw the line in the DAG. A vertical split separates layers of transformation. Examples are a shared staging project that others build on but cannot edit, an early project that masks PII before anyone downstream sees it, and an isolated set of expensive models that can't be selected by accident. A horizontal split separates by domain or source. Examples are the marketing team's data flow, clickstream events kept apart from transactional data, and two teams that work at different speeds. You can combine the two kinds, but pick one to do first. In a hub-and-spoke organisation, break out the central platform project before splitting the rest into domains.

    Good boundaries usually exist already. Look at which models your jobs build together, how the lineage graph clusters, and which resource groups people have defined in selectors.yml. A team that sits entirely downstream of the current project is often the easiest first consumer.

    Monorepo versus multi-repo for a multi-project mesh
    LayoutFits whenWatch for
    Monorepo (all projects in one repository)A smaller team mainly wants faster, simpler developmentBecomes unwieldy as projects, models and contributors grow
    Multi-repo (one repository per project)A larger organisation must separate projects for security and for teams with different development styles and rhythmsChanges across projects are harder, and should be treated like an API change

    Since Mesh launched, the most common pattern has been one project per team, each in its own repository. Some contributions across projects are healthy. If teammates need a coordinated change across several projects every week, or for a fifth or more of their work, the boundaries are in the wrong place.

    Checkpoint 1 of 8· Check yourself

    A security review requires that raw customer PII is masked before any analytics team can model it. Which split addresses this most directly?

    Checkpoint 2 of 8· Exam question

    The `analytics` project's `dependencies.yml` lists `finance` under a `projects:` block, and a model calls `{{ ref('finance', 'stg_orders') }}`. Every job in `analytics` fails to resolve that ref, even though `stg_orders` in the `finance` project is set to `access: public`. What is the most likely cause?

    Sources123

    2.Creating the new project in dbt

    Every project in a mesh is a complete dbt project with its own data platform connection, git repository and environments. To create one, go to Account settings, then Projects, and click New project. Enter a name, pick the data platform and fill in the connection settings, then click Test Connection before continuing. As a running example, take a fictional company that creates two projects this way: an upstream "Jaffle | Data Analytics" and a downstream "Jaffle | Finance", with the default databases jaffle_da and jaffle_finance. Connection testing has its own lesson: [testing a project connection](lesson:1.3).

    A project can join a mesh only once it has a deployment environment set as Production or Staging. Each project can have one deployment environment designated as Production. To create it, go to Deploy → Environments → Create New Environment, choose Deployment as the type, then select Production under Set deployment type. Which environment type cross-project references resolve against is covered in [environment types and cross-project references](lesson:7.2).

    Create a new project for a new codebase, never for another stage of the same codebase. Project names must be unique within a dbt account, and cross-project references find projects by name. Differences between dev and prod belong in environments. Each environment can use a different connection, connections can be reused across projects and environments, and one project can use several connections of the same warehouse type. That removes the usual reason teams create a duplicate project.

    Two things must be true before a downstream team can build on a new foundational project. At least one model must be defined as public, and a deployment job must have run successfully. That run produces the manifest.json file that downstream projects resolve their references against.

    Checkpoint 3 of 8· Check yourself

    A foundational project has public models and a working development environment, but nothing has ever run in deployment. A downstream team adds it as a dependency. What is missing?

    Checkpoint 4 of 8· Exam question

    A developer works in the `analytics` project and needs to build a marts model that reads from `stg_orders`, a public model owned by the upstream `finance` project (the `name` set in `finance`'s `dbt_project.yml`). Which line correctly authors that cross-project reference inside the `analytics` model's SQL?

    Sources45

    3.Moving models into the new project

    Before splitting anything, set up groups and access inside the monolith. Use groups to assign owners and set each model's access as tightly as current behaviour allows. Then run each group with selection syntax alongside your production jobs until you trust the boundaries. These settings are covered in [model governance](lesson:7.3). Once the boundaries are confirmed, splitting off one group follows a fixed checklist:

    1. Move the group's models into a subfolder, along with their YAML entries and any parent or child models that belong with them. 2. Create a new dbt_project.yml in that subfolder. 3. Copy any macros the moved models use. 4. Create a new packages.yml there for the packages those models need. 5. Change every ref that crosses projects to two arguments, project name then model name. Set the upstream models to access: public, ideally with a model contract. A contract sets explicit expectations on the shape of the data, so changes upstream do not break the downstream consumers who rely on it. 6. Add a dependencies.yml to the downstream project that names the upstream project.

    The downstream project's dependencies.yml after splitting jaffle_shopyaml
    # in dependencies.yml
    projects:
      - name: jaffle_shop

    Discipline matters more than speed here. Split one group at a time and aim for exact 1-to-1 parity with the old project. Note improvements for later instead of refactoring during the migration. Start inside the same repository, so git history gives you an easy way to undo the split. Circular references are still not allowed, and across projects the test is made per model: two projects may depend on each other in both directions, but a cycle exists when a model ends up depending on itself through models in either project. dbt checks for such cycles and raises an error.

    Some organisations already run several projects. They either install the parent project as a dbt package or read its outputs with source(). Both approaches have costs. A package puts every upstream resource into the child's manifest, which slows dbt down. Sources break the lineage, because nothing connects the two projects. Moving either setup to the mesh is simpler than splitting a monolith. In the parent, mark the referenced models public and add contracts. In the child, add the parent to dependencies.yml. Then, if you used a package, remove its packages.yml entry and add the project name to each ref. If you used sources, replace each source() with a cross-project ref and delete the source definitions you no longer need.

    Checkpoint 5 of 8· Check yourself

    A finance project currently reads the platform team's tables with source() calls. What does moving it onto the mesh require in the finance project?

    Sources315

    4.Declaring the dependency and writing the ref

    Packages and projects are two different kinds of dependency, and only project dependencies are part of the mesh. A package brings in its full source code, like a library. A project dependency brings in no code. The downstream project treats each upstream public model as an API that returns a dataset, and the model's maintainer is responsible for its quality.

    Package dependency versus project dependency
    AspectPackage dependencyProject dependency
    What you getThe package's full source code, added to your projectReferences to the upstream project's public models, resolved by a metadata service
    Filepackages.yml or dependencies.ymldependencies.yml only
    Upstream modelsParsed and runnable in your projectCan be selected, but your dbt run or dbt build cannot build them
    Upstream vars, env vars, target.nameMust be mirroredNo need to mirror
    Part of the Mesh workflowNoYes, on Enterprise and Enterprise+

    dependencies.yml sits at the root of the project and can hold both kinds of dependency: projects go under projects: and Hub packages under packages:. Keep using packages.yml when your package specifications use Jinja, for example a Git token read from an environment variable. dependencies.yml does not render Jinja and does not support private packages. The project name must match the upstream dbt_project.yml exactly, including case: jaffle_marketing, not JAFFLE_MARKETING.

    Checkpoint 6 of 8· Fill the gap

    The jaffle_marketing project needs dbt_utils as a package and the Finance team's models as a project. Which key completes this dependencies.yml?

    packages:
      - package: dbt-labs/dbt_utils
        version: 1.1.1
    
     ? :
      - name: jaffle_finance  # case sensitive and matches the 'name' in the 'dbt_project.yml'

    Every reference to a model from a project dependency uses the two-argument form, with the project name first. A one-argument ref looks only inside the current project. The upstream model must also be access: public. The default access is protected, which allows references only from within the model's own project.

    A two-argument ref to a public model in jaffle_financesql
    select * from {{ ref('jaffle_finance', 'monthly_revenue') }}

    Once you commit the change, dbt resolves the dependency, makes sure upstream projects are built first, and shows lineage across projects in the DAG. If the Finance team later renames the model, changes its schema or bumps its version, the ref still resolves. It points at wherever Finance builds the model in production.

    Checkpoint 7 of 8· Exam question

    A platform team is preparing the `finance` project's `stg_orders` model to be consumed by other dbt projects through a cross-project ref. Which two conditions must both be true before that ref will resolve for a downstream consumer? (Select two.)(Select 2)

    Sources54

    5.Projects that depend on each other

    Two projects can depend on each other in both directions, as long as no individual model ends up in a cycle. Finance can use Marketing's public models while Marketing uses Finance's. dbt checks for cycles across projects and raises an error if it finds one.

    A pair like this has to be set up step by step. Neither project can resolve a reference until the other has published public models from a deployment run. If you add both dependencies at once, whichever project runs first has nothing to resolve against.

    Checkpoint 8 of 8· Put it in order

    Put the setup of a two-way dependency between project_a and project_b in order.

    1. 1.project_b runs in a deployment environment and produces public models
    2. 2.project_a adds project_b as a dependency
    3. 3.project_b adds project_a as a dependency
    4. 4.project_a runs in a deployment environment and produces public models

    Sources5

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.You should give one team separate dbt projects for dev and prod, such as "Marketing - Dev" and "Marketing - Prod".Why is that wrong?

      Project names must be unique in the account. Separate dev and prod with environments, each of which can use its own connection.

      Covered in Creating the new project in dbt

    2. 2.Installing the upstream project through packages.yml sets up a mesh dependency.Why is that wrong?

      A package pulls in the full source code and is not part of the Mesh workflow. Cross-project references need a project entry in dependencies.yml.

      Covered in Declaring the dependency and writing the ref

    3. 3.Once the dependency is declared, a one-argument ref('monthly_revenue') finds the upstream model.Why is that wrong?

      Models from a project dependency must be referenced with the two-argument form that names the project.

      Covered in Declaring the dependency and writing the ref

    4. 4.Project names in dependencies.yml are matched without regard to case.Why is that wrong?

      The name must match the upstream dbt_project.yml exactly, including case.

      Covered in Declaring the dependency and writing the ref

    Practise it for real

    Make a downstream project reference a public model from an upstream project through a project dependency.

    1. 1.In the upstream project, set access: public on the model you want to share, merge the change, and run a deployment job in the Production environment.

      Why: Downstream references resolve only to public models, using metadata from a successful deployment run.

      You should see: The job succeeds and the model shows as Public in the lineage view.

    2. 2.In the downstream project, create dependencies.yml at the root and list the upstream project's name under projects:.

      Why: This declares a project dependency instead of installing the upstream code as a package.

      You should see: The name matches the upstream dbt_project.yml exactly, including case.

    3. 3.In a downstream model, select from the upstream model with a two-argument ref: project name first, then model name.

      Why: Project dependencies require the project name in the ref.

      You should see: The model compiles against the upstream model's production location.

    4. 4.Commit the changes and check the downstream lineage.

      Why: Once the dependency is configured, dbt resolves it and shows lineage across projects.

      You should see: The upstream public model appears as a parent in the DAG.

    Stuck? Get a nudge

    If the ref fails to resolve, check whether the upstream model still has the default access, protected.

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “The number of models in your project is degrading performance and slowing down development.”
      ↩︎ When a second project is worth it, and where to cut
      “{{ ref() }}s now work across dbt projects on Enterprise and Enterprise+ plans.”
      ↩︎ When a second project is worth it, and where to cut
      “If you're just starting your dbt journey, don't worry about building a multi-project architecture right away.”
      ↩︎ When a second project is worth it, and where to cut
      “data contracts set explicit expectations on the shape of the data”
      ↩︎ Moving models into the new project
    2. 2.
      “Horizontal splits separate your DAG based on source or domain.”
      ↩︎ When a second project is worth it, and where to cut
      “handle breaking out the central platform project before you split the remainder into domains.”
      ↩︎ When a second project is worth it, and where to cut
      “projects are 1:1 aligned to teams, and each project has its own codebase in its own repository.”
      ↩︎ When a second project is worth it, and where to cut
      “a coordinated change across multiple projects very frequently (every week), or as a key prerequisite for ~20%+ of their work.”
      ↩︎ When a second project is worth it, and where to cut
      “Isolating earlier models for security and governance requirements to separate out and mask PII data so that downstream consumers can't access it”
      ↩︎ Checkpoint
    3. 3.
      “Your selectors(defined in selectors.yml): How do people already define resource groups?”
      ↩︎ When a second project is worth it, and where to cut
      “Create a new dbt_project.yml file in the subdirectory.”
      ↩︎ Moving models into the new project
      “Do one group at a time! Do not refactor as you migrate, however tempting that may be.”
      ↩︎ Moving models into the new project
      “If using sources, there are breakages in the lineage, as there's no real connection between the parent and child projects.”
      ↩︎ Moving models into the new project
      “Replace the {{ source() }} functions with cross project {{ ref() }} functions. Remove the unnecessary source definitions.”
      ↩︎ Checkpoint
    4. 4.
      “From Account settings, go to Projects. Click New project.”
      ↩︎ Creating the new project in dbt
      “You must set up a "Production" or "Staging" deployment environment for each project you want to "mesh" together.”
      ↩︎ Creating the new project in dbt
      “Create and define at least one model as “public””
      ↩︎ Creating the new project in dbt
      “the always-enterprising and fictional account "Jaffle Labs" will create two projects for their data analytics and finance team”
      ↩︎ Creating the new project in dbt
      “By default, model access is set to "protected", which means they can only be referenced within the same project.”
      ↩︎ Declaring the dependency and writing the ref
    5. 5.
      “Each project name must be unique in your dbt account.”
      ↩︎ Creating the new project in dbt
      “Connections are reusable across projects and environments.”
      ↩︎ Creating the new project in dbt
      “so long as the new dependency doesn't introduce any node-level cycles.”
      ↩︎ Moving models into the new project
      “You don't need to mirror any conditional configuration of the upstream project such as vars, environment variables, or target.name.”
      ↩︎ Declaring the dependency and writing the ref
      “While you can select those models, you can't actually build them.”
      ↩︎ Declaring the dependency and writing the ref
      “Private packages are not supported in dependencies.yml because they intentionally don't support Jinja rendering or conditional configuration.”
      ↩︎ Declaring the dependency and writing the ref
      “dbt checks for cycles across projects and raises errors if any are detected.”
      ↩︎ Projects that depend on each other
      “When setting up projects that depend on each other, it's important to do so in a stepwise fashion.”
      ↩︎ Projects that depend on each other
      “Using a metadata service that runs behind the scenes, dbt resolves references on-the-fly to public models defined in other projects.”
      ↩︎ Key concept
      “avoid creating projects for Jaffle Marketing - Dev and Jaffle Marketing - Prod; use environment-level isolation instead.”
      ↩︎ Exam trap 1
      “Something to note is that it doesn't contribute to the dbt Mesh workflow.”
      ↩︎ Exam trap 2
      “Models referenced from a project-type dependency must use two-argument ref, including the project name”
      ↩︎ Exam trap 3
      “the project name must exactly match the name in your dependencies.yml. For example, jaffle_marketing, not JAFFLE_MARKETING.”
      ↩︎ Exam trap 4
      “avoid creating projects for Jaffle Marketing - Dev and Jaffle Marketing - Prod; use environment-level isolation instead.”
      ↩︎ Prediction
      “Define a Production deployment environment in the upstream project and make sure at least one deployment job has run successfully there.”
      ↩︎ Checkpoint
      “Each project must run and produce public models before the original producer project can take a dependency on the original consumer project.”
      ↩︎ Checkpoint

    Ready to test yourself?

    Practise the 9 questions on this subdomain.

    Spotted a mistake, or was something unclear? Tell us.