The Doe family project structure
How the Doe family organizes their dbt project — directories, dbt_project.yml, and the sources → staging → marts layout that aligns with how nexus is consumed.
Learning Objectives
By the end of this lesson, you will be able to:
- Identify the standard directories in a dbt project
- Read and edit
dbt_project.yml - Understand the
sources → staging → martsmodel layout - Recognize what
packages.ymlis for (preview of Module 3)
The project skeleton
After dbt init (or initializing in dbt Cloud), your project looks
roughly like this:
doe_family/
├── analyses/
├── macros/
├── models/
├── seeds/ ← family_members.csv lives here (lesson 2.1)
├── snapshots/
├── tests/
├── dbt_project.yml
├── packages.yml ← you'll add this in Module 3
└── README.md
| Directory | What lives there |
|---|---|
models/ |
Your SQL transformations. Each .sql file becomes a table or view. |
seeds/ |
CSV files. dbt seed loads them as tables (covered in 2.1). |
macros/ |
Reusable Jinja functions (2.6). |
tests/ |
Custom data tests beyond the built-in ones. |
snapshots/ |
SCD2-style history capture for source tables (not used in this course). |
analyses/ |
Ad-hoc SQL files dbt compiles but doesn't run. Rarely used. |
The two you'll touch most are models/ and seeds/.
dbt_project.yml
The control file for the whole project. The Doe family's looks like this:
name: doe_family
version: 1.0.0
config-version: 2
profile: doe_family # references the profile in ~/.dbt/profiles.yml
# (or, in dbt Cloud, the profile bound to the project)
model-paths: ["models"]
seed-paths: ["seeds"]
test-paths: ["tests"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
analysis-paths: ["analyses"]
clean-targets:
- "target"
- "dbt_packages"
models:
doe_family:
sources:
+materialized: view
staging:
+materialized: view
marts:
+materialized: table
vars:
# Populated in Module 3 when we install dbt-nexus
Two things to notice:
- The
models:block sets defaults by directory. Anything undermodels/sources/is a view; anything undermodels/marts/is a table. Individual models can override with{{ config(...) }}— see 2.4 Materializations. - The
vars:block is where dbt-nexus configuration will land in Module 3.
The sources → staging → marts layout
The Doe family lays out models/ in three layers:
models/
├── sources/ # one folder per source system; raw → standardized
│ ├── gmail/
│ ├── google_calendar/
│ └── notion/
├── staging/ # nexus-shaped intermediate models (events, identifiers, traits)
│ ├── gmail/
│ ├── google_calendar/
│ └── notion/
└── marts/ # the family-facing outputs
├── family_contacts.sql
└── christmas_card_list.sql
Why this layering matters:
| Layer | Purpose | Materialization |
|---|---|---|
sources/ |
Light cleaning of raw warehouse data (rename, cast, dedupe) | view |
staging/ |
Shape source data into nexus's required outputs (events, etc.) | view / table |
marts/ |
The tables the family actually queries | table |
You don't have to use this exact layout — but it mirrors how nexus is designed to be consumed, which makes the transition into Module 3 seamless.
The seed you loaded in 2.1 lives in
seeds/, not models/, but downstream marts/ can ref() it the
same as any model.
packages.yml (preview)
In Module 3 you'll create packages.yml to install dbt-nexus. It
isn't on the dbt Hub, so it installs directly from Git:
packages:
- package: dbt-labs/dbt_utils
version: 1.3.0
- git: https://github.com/slide-rule-tech/dbt-nexus
revision: v0.9.1
dbt deps installs declared packages into dbt_packages/ (which your
.gitignore already excludes). Packages can ship models, macros, and
tests; dbt-nexus ships all three. See 2.6 Macros and packages
for the deeper take.
Hands-On Exercise
-
Create the directory structure above:
mkdir -p models/sources models/staging models/marts -
Open
dbt_project.ymland add themodels:block with per-directory materialization defaults shown above. -
Create
models/marts/family_size.sql:select count(*) as n_members, countif(role = 'parent') as n_parents, countif(role = 'child') as n_children from {{ ref('family_members') }} -
Run
dbt build -s family_sizeand check the result in BigQuery. Confirm it's atable(yourmarts/default), not aview. -
Commit the changes on a feature branch, push it to GitHub, and open a PR (1.5 muscle memory).
Summary
| Concept | Key takeaway |
|---|---|
| Project skeleton | models/, seeds/, macros/, tests/, dbt_project.yml |
dbt_project.yml |
Project config + per-directory model defaults |
| Layered models | sources → staging → marts keeps raw, shaped, and final cleanly separated |
| Why this layout | Mirrors how nexus is consumed; sets you up for Module 3 |
packages.yml |
Where you'll declare dbt-nexus in Module 3 |
Next Lesson
You've got dbt set up, the project structured the way nexus expects, and every core concept covered. Last stop in Module 2: 2.8 Examples and practicum — three worked examples and a consolidated practicum to lock everything in before we dive into nexus.