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¶
runtime (WSGI server)
→ edges process entry point; settings name the inits middleware by string
→ inits middleware builds Services per request, attaches them to the request
→ gates handler typed RootRequest calls request.services.<name>
→ mills service enforces the rule via repo protocols + specs constants
→ links repository queries the store, returns a DTO
← gates renders the DTO — template, serializer, stdout
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.