Codebase Organization¶
Top-Level Layout¶
dbwarden/ # The package itself
tests/ # Test suite (~40 modules)
docs/ # Documentation site (MkDocs)
examples/ # Runnable example projects
scripts/ # Development and CI tooling
assets/ # Images, icons, branding
site/ # Built documentation output (gitignored)
Package Layout (dbwarden/)¶
| Directory / Module | Responsibility |
|---|---|
cli/ |
Typer CLI definitions, argument parsing |
commands/ |
Command orchestration (migrate, generate-models, check, etc.) |
engine/ |
Core logic: model discovery, snapshot extraction, diff, offline migration, safety checks |
database/ |
Connection management, SQL queries by dialect |
databases/ |
Concrete backend specs: ClickHouse, MySQL, PostgreSQL, MariaDB, SQLite |
schema/ |
Dialect-agnostic metadata layer: table/column/field metadata classes |
repositories/ |
Migration and lock metadata persistence |
config*.py |
Configuration loading and resolution |
constants.py |
Shared constants |
exceptions/ |
Exception hierarchy |
seed.py |
Seed data infrastructure |
engine/sandbox.py |
Module loading sandbox for user model files |
The schema/ vs databases/ Boundary¶
The dbwarden/schema/ package is the abstract metadata layer. It defines dialect-agnostic constructs that make no assumptions about the target database:
TableMetaand*ColumnMetaclasses (e.g.PGColumnMeta,CHColumnMeta,MyColumnMeta)DBWardenMeta: the runtime metadata container attached to each model_MetaValidator: metaclass that validatesclass Metaattribute names at import timeIndexSpec,CheckSpec,UniqueSpec: cross-database object specs_meta_reader.py: logic that readsclass Metafrom user models and populatesDBWardenMeta
The dbwarden/databases/ package is the concrete backend layer. It contains dialect-specific specs and helpers:
clickhouse/:ChEngineSpec,ProjectionSpec,ChIndexSpec,ChTableSpec, merge-tree helpers,ChFieldSpecmysql/:MyFieldSpec,MyTableSpecpgsql/:PgFieldSpec,PgIndexSpec,PgTableSpec, exclude/partition helpersmariadb/:MdbFieldSpec,MdbTableSpecsqlite/:SqFieldSpec,SqTableSpec, re-exportedSqTableMeta/SqColumnMeta
The Import Contract¶
The single most important rule in the codebase is:
schema/must never import fromdatabases/.
This keeps the metadata layer database-agnostic. databases/ may import from schema/ (and does, for TableMeta, DBWardenMeta, IndexSpec, etc.), but the reverse dependency is forbidden.
Consequences of this boundary:
ChEngineSpecandProjectionSpeclive indatabases/clickhouse/, notschema/. They are ClickHouse-specific types, not abstract schema concepts.- Backend specs (
ChTableSpec,MyTableSpec, etc.) are defined per-database, not inschema/. - The
schema/__init__.pyonly re-exports classes fromschema/submodules. It does not re-export backend-specific types fromdatabases/. - Users import backend types through
from dbwarden.databases.clickhouse import ChEngineSpecor the top-levelfrom dbwarden import ChEngineSpec.
What Changed in the v0.13.0 Refactor¶
The refactor tightened this boundary. Previously, ChEngineSpec, ProjectionSpec, and the *FieldMeta hierarchy lived in schema/. They were moved to their correct locations:
ChEngineSpec,_split_engine_args: now indatabases/clickhouse/engine.pyProjectionSpec: now indatabases/clickhouse/projection.py*FieldMetaclasses (PGFieldMeta,CHFieldMeta, etc.): deleted; fields inlined directly into*ColumnMetaintable_meta.py
The orphan __pycache__ directories under schema/{clickhouse,mysql,pgsql,mariadb,sqlite}/ were removed.
Contribution Guidelines¶
Before submitting a PR¶
- Ensure your changes respect the
schema/vsdatabases/import boundary (see above). - Run the full test suite before pushing:
- If you add or remove a public export, update the corresponding
__all__list in the module's__init__.py. - If you introduce a new top-level directory, add it to the table in this document.
Code style¶
- No comments in production code unless the logic is genuinely subtle.
- Mimic existing patterns: same typing style, same docstring conventions, same import organization.
- Prefer
from __future__ import annotationsat the top of every module. - Use
metaclass=_MetaValidatorfor any newclass Meta-like user-facing configuration class.
Adding a new database backend¶
- Create a new subpackage under
databases/<name>/with__init__.py,field.py, and any backend-specific specs. - Define a
*TableSpecdataclass and a*FieldSpecdataclass matching the existing backends. - Register the backend in
databases/__init__.pyand add the shortcut import (sq,my, etc.). - If the backend needs no column-level
Metaattributes, add no*ColumnMetaclass. - Do not touch files in
schema/unless you are adding cross-database metadata fields.