dbt-arch-unit
Architectural unit testing for dbt projects.
Like ArchUnit for Java or
import-linter for Python — but for
dbt. Declare the architecture your team already agreed on in a single
dbt_arch.yaml, and enforce it in CI and pre-commit.
# dbt_arch.yaml (next to dbt_project.yml)
layers:
staging: { paths: ["models/staging/**"], prefixes: ["stg_"] }
marts: { paths: ["models/marts/**"], prefixes: ["fct_", "dim_"] }
reporting: { paths: ["models/reporting/**"], prefixes: ["rpt_"] }
rules:
- name: expect-dependencies
# Flow chains: data moves left -> right, so "a > b" lets b depend on a.
# Adjacent only — list a skip ("staging > reporting") or same-layer ("marts > marts") explicitly.
allow: ["source > staging > marts > reporting", "marts > marts"]
deny: [] # blacklist edges; deny always wins over allow
- name: expect-max-lines-of-code
max: 200
- name: expect-primary-key
scope: [marts] # run this test only on the marts layer
- name: expect-no-select-star
ignore: [staging] # run everywhere except staging
- name: expect-model-name-convention
case: snake_case # or kebab-case / camelCase; also max_length / prefix / suffix
- name: expect-comments
forbid: ["TODO", "FIXME"] # allowed: true by default; also max_length / allow_block
- name: expect-min-tests-per-model # every model needs ≥ N data tests (min:1 = "has any")
min: 1
- name: expect-min-tests-per-source # every source needs ≥ N data tests
min: 1
- name: expect-max-models-per-layer
max: 20
scope: [reporting] # keep certain layers intentionally small
Every rule accepts scope: [layers] (run only on these layers) and
ignore: [layers] (run everywhere except these) — layer-based selectors, distinct
from the path-glob include: / exclude:. Rule params like max are written
inline; the older config: { max: 200 } form still works.
# after `dbt parse` (produces target/manifest.json)
dbt-arch-unit check # run all configured rules, exit 1 on violations
dbt-arch-unit check --json # machine-readable output for CI
dbt-arch-unit report -o report.html --open # full HTML report + open it
dbt-arch-unit list-rules # every available rule
dbt-arch-unit explain expect-dependencies
dbt-arch-unit init # validate this is a dbt project, then scaffold config
Installation
Requires Python 3.10+.
# once published to PyPI:
pip install dbt-arch-unit
pipx install dbt-arch-unit # isolated CLI install
uv tool install dbt-arch-unit # via uv
# from source (available today):
uv tool install git+https://github.com/dardanxh/dbt-arch-unit
pipx install git+https://github.com/dardanxh/dbt-arch-unit
# for local development:
git clone https://github.com/dardanxh/dbt-arch-unit
cd dbt-arch-unit
uv sync --extra dev
uv run dbt-arch-unit --help
Use as a pre-commit hook
Add to your dbt project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/dardanxh/dbt-arch-unit
rev: v0.1.0
hooks:
- id: dbt-arch-unit
Severity & CI
Every rule has a severity — error (default) or warning. Set it per rule, or change the
project-wide default:
defaults:
severity: error # applied to any rule that doesn't set its own
rules:
- name: expect-no-select-star # error (inherits the default)
- name: expect-min-tests-per-model
severity: warning # reported, but never fails CI
dbt-arch-unit check exits 1 if there is at least one error-severity violation, and
0 otherwise — warnings alone never fail the job. Run it in CI right after dbt parse:
# .github/workflows/ci.yml
- run: dbt parse # produces target/manifest.json
- run: dbt-arch-unit check # non-zero exit on any error -> job fails
Use dbt-arch-unit check --warn-only to always exit 0 (report without failing).
How it works
Hybrid parsing: target/manifest.json supplies the accurate dependency graph,
configs, tags, columns and tests; raw .sql/.yml files supply line counts, CTE
structure, select * usage and joins. Each rule is a small, self-contained
function. See dbt-arch-unit list-rules for the full catalog.
init — guarded scaffolding
dbt-arch-unit init first checks that the target directory is actually a dbt
project before writing anything:
dbt_project.ymlexists and parses, and declares aname(required),- the
model-pathsdirectory exists (required), - it contains
.sqlmodels and a compiledtarget/manifest.json(advisory).
If the required checks fail, no file is written and it exits non-zero. On
success it auto-detects your models/ layer folders (staging, intermediate,
marts, reporting, …) and writes a dbt_arch.yaml tailored to them.
dbt-arch-unit init # inspect ./ and scaffold
dbt-arch-unit init --project-dir path/to/dbt
dbt-arch-unit init --force # overwrite an existing config
HTML report
dbt-arch-unit report runs the checks and writes a single, self-contained
.html file (no external assets) with:
- a pass/fail banner and headline stats (total issues, errors, warnings),
- percentages — % of models affected and % of rules passing,
- bar-chart breakdowns of issues by category, by rule, and by severity,
- the full findings table (severity, rule, location, message).
dbt-arch-unit report -o architecture_report.html # write the report
dbt-arch-unit report -o report.html --open # and open it
dbt-arch-unit check --html report.html # table + report in one go
Rule catalog
41 rules across six categories — dependencies, naming, testing,
documentation, style, and materialization governance. Run dbt-arch-unit list-rules to see them all, or dbt-arch-unit explain <rule> for details and
config keys.
Contributing
Contributions are very welcome — especially new rules. See CONTRIBUTING.md for the dev setup and a walkthrough of adding a rule, and please follow the Code of Conduct.
License
MIT © Dardan Xhymshiti
Metadata
Release files for dbt-arch-unit 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dbt_arch_unit-1.0.0.tar.gz | 96.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dbt_arch_unit-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 134.3 kB
Release files / dbt_arch_unit-1.0.0.tar.gz
| Download URL | dbt_arch_unit-1.0.0.tar.gz |
|---|---|
| Size | 96.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f72d95569c8ba51b5c0cc860a5b8b01b9c4dcdece0900c75ecf2fc73111dbde1
|
|
BLAKE2b-256 checksum How to use checksums |
3e50a35ed3cd341b69897f0b44b7a36e47f2b88af6334360d54d1e3d53ec308b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency logRelease files / dbt_arch_unit-1.0.0-py3-none-any.whl
| Download URL | dbt_arch_unit-1.0.0-py3-none-any.whl |
|---|---|
| Size | 37.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ea8ca2ec79185c67c5a21d712a600619f354e67c1fbec756c71c26ae32858bf
|
|
BLAKE2b-256 checksum How to use checksums |
e83ff91a78eda5b50be18fbc538f5f6e4fb25cc0720f1aed80cf0416718389c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency log