---
title: "dbt sources read directly by several models | Metacenta"
description: "When several dbt models read the same raw source, each casts and renames it separately and the copies drift. How to route them through one staging model."
url: "https://metacenta.com/rules/source-fanout"
updated: "2026-10-14"
tokens: 693
---

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, staging included. 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:

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

After:

```sql
-- 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 don't. It doesn't need a layered project.

## Published rules it corresponds to

**[dbt_project_evaluator](https://dbt-labs.github.io/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.

[Ask about a review](https://metacenta.com/enquire?src=rule-source-fanout&via=md)

## Related pages

- [dbt incremental models: how unique_key works](https://metacenta.com/rules/incremental-without-unique-key)
- [dbt public models with no enforced contract](https://metacenta.com/rules/public-model-without-contract)
- [dbt sources declared twice for one table](https://metacenta.com/rules/duplicate-source)
- [dbt staging models: how to set up the staging layer](https://metacenta.com/rules/staging-depends-on-staging)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
