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. This page mirrors the current
state for readers, but the CI gate reads the YAML directly 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#
Source layer |
Allowed import targets |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Documented tolerances#
Tolerances live in layer_matrix.yaml and must carry a rationale.
They are temporary or deliberately narrow edges. A contributor should
tighten them when the underlying edge disappears.
Current tolerances cover:
root lazy resolution of
project;comparison exports reusing display helpers;
comparison orchestration writing an HTML report;
FlowConfigembedding a spatial discriminated union;core time validation using flow-regime semantics;
read-only cross-DB bridges between data cache and results catalog.
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.validity_frameExperimental observability tooling. It is installed with the package but isolated from the modeling DAG and is not a stable V1 public API.
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.