We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Seams, Modules, Packaging and Tooling step 11 of 36
The layering contract, as a test
Write the fitness function that keeps a layered architecture from decaying into a directory.
def check_layering(
module_layers: Mapping[str, str],
layer_order: Sequence[str],
imports: Mapping[str, Sequence[str]],
) -> list[tuple[str, str]]:
-
module_layersmaps a module name to the name of its layer. -
layer_orderlists the layers innermost first, e.g.["domain", "application", "adapters", "entrypoints"]. -
importsmaps a module to the modules it imports.
Return every (importer, imported) pair that points outward — where the
imported module’s layer sits later in layer_order than the importer’s.
The rules that make it usable in a real repository:
-
Same-layer imports are legal.
domain.orderimportingdomain.moneyis not a finding. Only strictly-outward edges are. -
Unknown modules are ignored, on both sides.
decimal,httpxand a one-off script that is not inmodule_layersproduce no findings. A contract that fires on every stdlib import is a contract that gets disabled. -
A layer not present in
layer_orderis ignored too. That is how you park an unclassified area of the codebase without switching the check off for everything else. - The result is deduplicated and sorted. A module listed twice in the same import list is one violation, and the output must not depend on dict ordering — an unstable list means a diff nobody can review.
What you are actually building. This is import-linter‘s layers
contract in fifteen lines. The value is not the algorithm; it is that once
check_layering(...) == [] is an assertion in your suite, “the domain must
not import the database” stops being a code-review opinion that a tired
reviewer waves through and becomes a red build on the branch that broke it.
Note what the direction buys you: with zero outward edges, domain can be
imported in a fresh interpreter with sqlite3 and httpx unavailable. That
is a test you can write, and it is the real proof that the layering is more
than a folder structure.
Your submission must pass mypy --strict. module_layers.get(name) returns
str | None and both of those Nones are load-bearing rules, not oversights
to silence.
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.