Request Lifecycle¶
One HTTP request, traced through every layer. A CLI command follows the same
shape with a different opening: nothing in edges, no middleware —
pyproject.toml names inits by dotted string, and inits constructs the
gate directly, injecting the mills into its constructor.
The trace¶
flowchart TD
runtime["runtime<br/>WSGI server"]
runtime -- "loads entry point" --> edges
edges -. "names middleware by string" .-> inits
inits -- "attaches Services to request" --> gates
gates -- "request.services.proposals" --> mills
mills -- "via repo protocol" --> links
links -. "DTO" .-> mills
mills -. "DTO, rendered" .-> gates
Step by step¶
- The runtime invokes
edges. A WSGI server loadsedges/wsgi.py; nothing in the codebase importsedges. Settings listmyproject.inits.ServicesMiddlewareas a dotted string — configuration, not an import. initsbuilds the object graph. Per request, the middleware constructsServices()— which builds its ownRepositories()— and attaches it to the request. Every leaf is a@cached_property, so only what the request touches gets built.- A gate handles the request. The handler types the request as
RootRequest— the gate-local typing subclass — and callsrequest.services.proposals.get(pk). Its only project import ispacts: nothing frommills,links, orinits. - A mill runs the business rule. The service sees repository protocols from
pactsand constants fromspecs. If it writes to more than one repository, it openstransaction.atomic()itself — the gate never does. - A link touches the store. The repository queries the ORM and returns a DTO. The ORM instance never leaves the adapter.
- The gate renders the DTO. Template, serializer, or stdout — always the
pactsshape, never a model.
Six layers, one straight line, every arrow checked by importlinter.