Metacenta

Modelling correctness · rule source-fanout

dbt sources read directly by several models

A Metacenta review checks this under the rule Each source is read through one model. Everything below applies whether or not you ever commission one.

What this rule checks

This rule flags any source table that more than one model reads directly with source(). It counts readers in every layer, not only staging. We report it once against the source, rather than once per reading model.

Why it matters

Each reader casts and renames the raw columns for itself, so a change to the source has to be found in several places. Two readers can already disagree without anything failing.

How to fix it

Read each raw source through one cleaning step. Add one staging model over the source, and point every reader at it with ref().

Before:

-- fct_orders.sql and int_order_items.sql both read:
from {{ source('shop', 'orders') }}

After:

-- stg_shop__orders.sql reads the source once:
from {{ source('shop', 'orders') }}

-- fct_orders.sql and int_order_items.sql read:
from {{ ref('stg_shop__orders') }}

When it is fine to leave

A versioned pair of staging models over one source, such as stg_orders_v1 and stg_orders_v2, is fine during a migration. We rate this rule low severity, because the copies may still agree.

What we need to check it

manifest.json alone. Only models count as readers; tests and snapshots do not. It does not need a layered project.

Published rules it corresponds to

dbt_project_evaluator, rule fct_source_fanout. Ours checks everything it flags, and more. dbt's guidance is one staging model per source. This check counts every model that reads the source directly, in any layer, so it can report a fan-out their test does not. We claim it as broader, not equivalent, so a client who runs their package gets no surprise.

This means our check corresponds to their rule. It does not mean the publisher reviewed or endorses it.