Unit 07.04: Where dependency injection stops helping
Dependency injection is for lifecycles and boundaries, not for business logic.
Six uses, three of them wrong
What belongs in a dependency and what does not.
The code sorts them.
LIMITS = [
("a shared database session", "good", "per-request lifecycle is the point"),
("settings and clients", "good", "built once, injected, overridable"),
("an auth check", "good", "declarative, and testable"),
("business logic", "BAD", "belongs in services/, callable without HTTP"),
("a chain six dependencies deep", "BAD", "the request path becomes untraceable"),
("something a plain function call would do", "BAD", "indirection with no benefit"),
]
print(f"{'used for':34} {'verdict':8} why")
for use, verdict, why in LIMITS:
print(f"{use:34} {verdict:8} {why}")
print("""
Dependency injection is for things with a lifecycle or a boundary: sessions,
clients, credentials, settings. Business logic put there becomes reachable only
through HTTP, which is exactly what the service layer exists to prevent.
""")
Business logic in a dependency becomes reachable only through HTTP, which is exactly what the service layer exists to prevent. It also becomes invisible in the handler - a reader sees a parameter and has to go elsewhere to find out what it does.
The six-deep chain is the other failure. Each level is reasonable and the request path becomes impossible to follow, which is worst during an incident.
The mistake this prevents
The mistake is using dependencies as a general dependency-injection container. They are a request-scoped mechanism for things with a lifecycle - sessions, clients, credentials - and everything else is better as a plain function call.
Takeaway
Use dependencies for lifecycles and boundaries: sessions, clients, settings, credentials. Business logic belongs in services that need no request to run.
