Layers and import direction
An application grows in layers. The domain holds the business rules, the service layer runs use cases, and adapters talk to databases, files and networks. One rule keeps them apart: outer layers may import inner ones, never the reverse.
When the domain imports a database adapter, every test of a business rule needs that database, and a change of storage ripples into the rules. Keep the domain importing only the standard library and itself.
Checking the direction
Python resolves imports while the code runs, so nothing stops a wrong one. A test can: parse each module with ast, collect what it imports, and compare the layers.
import ast
source = ("import json\n"
"from adapters.db import save")
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.Import):
print([a.name for a in node.names])
if isinstance(node, ast.ImportFrom):
print(node.module)Tools such as import-linter run the same kind of check in CI.
Your task
Write imported_modules(source) returning the sorted, unique module names a source imports (import a.b and from a.b import c both give "a.b"; relative imports are ignored). Write violations(modules, layers): modules maps names like "domain.orders" to source text, layers lists layer names from innermost to outermost, and the result is a sorted list of (module, imported) pairs where a module imports from a layer further out than its own.
The explanation opens here once every check passes.