Layered Architecture#
HydroModPy is built as a strict layered DAG. Every top-level subpackage
of hydromodpy/ belongs to one layer and may only import from the
targets declared in tests/unit/architecture/layer_matrix.yaml.
The YAML file is the normative contract, and the tables below are
generated from it by tools/doc_contracts.py at every build, so they
cannot drift from it. The CI gate reads the same YAML through
tests/unit/architecture/test_layer_matrix.py.
Ports and adapters at the storage edge#
The V1 storage contract is DuckDB-first, not backend-agnostic in every
runtime path. The project catalog uses
CatalogBackend, with
DuckDBBackend as the in-tree implementation. The data cache uses a
separate DuckDB cache adapter. CLI diagnostics, migration runners and
portable-package snapshots may open DuckDB directly when documented as
exceptions in Storage Layout.
Field readers go through hmp.read which dispatches to Zarr or
Parquet stores via the field registry. See
results for the Python surface.
The rules#
One layer per top-level subpackage. Cross-edges that violate the matrix fail CI.
One-way dependencies only. No cycles, even under
TYPE_CHECKING.coreis the kernel leaf. It must not import any sibling layer.Each MODFLOW backend is independent.
solver/modflow6/andsolver/modflow_nwt/never cross-import.hydromodpy_annex/may importhydromodpy/. The reverse is forbidden.Cross-package imports of underscored modules are forbidden. Leading underscore means private to the owning package.
A new edge that violates the matrix is a regression.
Layer matrix#
19 layers, 148 granted edges. A layer always implicitly allows imports targeting itself; anything absent from a row is forbidden and fails CI.
Source layer |
Allowed import targets |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Documented tolerances#
7 edges are tolerated rather than granted. Each one carries its reason in the contract. They are temporary or deliberately narrow; tighten one when the edge disappears.
From |
To |
Why |
|---|---|---|
|
|
root facade lazy-resolves Project through hydromodpy.project |
|
|
comparison experiment_launcher writes the HTML report at the end of the orchestration (candidate to move into workflow/) |
|
|
FlowConfig embeds spatial FieldSection discriminated union |
|
|
cross-DB ATTACH bridge: data.DataEntry.used_by reads results.cross_db (commit 1bd5f31ef) |
|
|
cross-DB ATTACH bridge: results.cross_db reads DataCatalogDuckDB via ATTACH read-only (commit 1bd5f31ef) |
|
|
site-selection BD Topage outlet snapping delegates the optional hydrography fetch to data managers through a narrow helper |
|
|
network-transient calibration diagnostics reuse the shared static HTML report-block renderer |
Files exempt from the layer rule#
5 files are exempt: bootstrap shims that wire forward references, and the case
runners that load a user TOML and belong semantically to examples/.
hydromodpy/__init__.pyhydromodpy/_bootstrap.pyhydromodpy/__main__.pyhydromodpy/spatial/domain/cases/run_domain_case.pyhydromodpy/spatial/geographic/cases/reference_catchment_delineation_case/run_case.py
Special layers#
catalogPublic V1 facade over cache, project catalog and global index. It may import
dataandresultsto wrap their stores. The reverse edge is forbidden.projectPublic object-oriented facade. It sits above the matrix like
cliso lower layers do not depend onProject.
How CI checks the matrix#
tests/unit/architecture/test_layer_matrix.py parses every Python
file in hydromodpy/ and asserts each edge is either allowed or
tolerated. It also checks that every declared package has a per-package
architecture page.
When refactoring across layers#
If a refactor needs a new edge that the matrix forbids:
Look for an existing intermediary layer.
If none fits, propose the change before touching code.
Update the YAML and the prose together.
Never add a tolerance silently.
See also#
Package Layout for the role of each layer.
Mental Model & Design Choices for the runtime flow that the matrix shapes.
Code Reading Guide for the package-by-package reading order.