Course outline

dbt / Basic: project and model foundations / Lesson 2

Sources, staging and dependency graphs

Declare library inputs and model relationships without hiding dependencies behind hardcoded relation names.

About 4 min readContent reviewed
On this page

Lesson goals

  • Use source and ref for distinct resource roles.
  • Review graph order, cycles and environment resolution.

A source declaration describes an external relation

The library’s raw loan table is loaded by an upstream process. Declaring it as a source gives dbt a named input for lineage, tests and optional freshness checks; it does not copy the table into existence. Keep the source owner and physical location separate from its logical name. A development environment can point to an approved different database while downstream model SQL still calls the same logical source. That portability requires deliberate configuration and permissions. A successful name resolution does not establish that raw data is current, complete or correctly typed.

Make the logical and physical names visible

The sources.yml fragment names a fictional raw_library schema and a loans table. Its version key describes the YAML resource-file format, not the database version or a source-history revision. Real projects can configure database, schema and identifier according to the approved deployment. Avoid putting credentials or private endpoints into source descriptions. Optional source tests are separate assertions, and freshness is a separate temporal signal. Neither should be interpreted as universal business completeness. The configuration is illustrative only and is not parsed or executed by dbt during authoring.

yaml Illustrative technical example; logical results are reasoned, not engine-tested.
version: 2
sources:
  - name: library
    schema: raw_library
    tables:
      - name: loans

A resource declaration for an already existing fictional relation. It creates no warehouse object and proves no data freshness.

Stage one source with a narrow interpretation contract

A staging model commonly renames columns, chooses types and standardizes controlled values near the source boundary. The fragment uses source('library','loans') rather than a hardcoded schema.table string. Its single-source projection does not promise every source record is valid; explicit parsing and rejection policy remain necessary. Avoid burying cross-source business aggregation in a staging model merely to reduce the number of files. Clear layers make it easier to identify whether a wrong open-loan total began in source interpretation, relationship modeling or final report logic.

jinja Illustrative technical example; logical results are reasoned, not engine-tested.
select
    loan_id,
    branch_id,
    borrowed_at,
    returned_at
from {{ source('library', 'loans') }}

A staging projection only. Source schema, key uniqueness and timestamp meaning are explicit upstream assumptions.

A dependency is executable scheduling knowledge

ref and source record relationships in the project graph. If fct_loans depends on stg_loans, and daily_branch_activity depends on fct_loans, the prerequisite order follows those edges. A graph can have several valid topological orders for independent nodes; a cycle has none. Hardcoded warehouse names can hide a dependency from dbt even when the SQL engine can read them. Conditional Jinja can also complicate dependency discovery, so keep required relationships visible using supported patterns. A graph edge specifies prerequisite structure, not that upstream data passed every quality or freshness check.

Review environment targets without rewriting business SQL

Logical references resolve through resource naming, target settings and supported deferral behavior. A developer seeing a production relation in compiled SQL should inspect that context rather than assume ref always means “this developer’s schema.” Use separate approved environments and least-privilege principals, then verify selected relation names before any authorized execution. This course contacts no database and performs no dbt parse, compile or graph command. Its local validation can check a tiny declared DAG for cycles and expected edges, which is a model of dependency reasoning rather than proof that dbt parsed a real project.

Common errors and fixes

A declared source is assumed to have been ingested

Why it happens
Resource metadata was confused with upstream loading.
How to fix it
Verify the approved source relation and ingestion evidence.
Prevent it next time
Document source ownership and delivery expectations.

Hardcoded relation names disappear from lineage

Why it happens
SQL references bypassed ref or source graph declaration.
How to fix it
Use the appropriate logical resource reference and review dependencies.
Prevent it next time
Check graph edges during model review.

The model graph contains a cycle

Why it happens
Two transformation layers depend on each other directly or indirectly.
How to fix it
Separate shared inputs or redesign the layer boundary.
Prevent it next time
Keep a clear prerequisite direction and cycle checks.

Check your understanding

1. Does source("library","loans") create raw_library.loans?

Show worked solution

No. It resolves a declared external relation and records lineage. The approved upstream process must create and populate that source. Verify its schema, delivery and permissions separately, then apply tests and freshness checks according to their precise scope.

2. What order is required for staging→fact→daily report?

Show worked solution

The staging prerequisite precedes the fact, and the fact precedes the report. Independent branches can run in other valid orders. A cycle would prevent a topological order; hiding a reference in a hardcoded name does not solve the underlying dependency.

Key takeaways

  • Sources describe external inputs; refs connect modeled resources.
  • Lineage, physical naming and source quality require separate evidence.

Official references