How is DI implemented in Clean Architecture?
In Clean Architecture, dependency injection (DI) is how the Dependency Rule is implemented in practice: inner layers define abstractions, while outer layers supply concrete implementations for them.
1. Principle
Business logic (use cases, entities) does not create its own dependencies. Everything it needs is passed in from outside.
This lets inner code work with interfaces without knowing which repository, API, or service is actually behind them.
2. What it looks like by layer
Inner layer (Use Case)
Defines interfaces, but not their implementation:
class OrderRepository(Protocol):
def save(self, order): ...Outer layer (Adapter / Infrastructure)
Implements those interfaces:
class SqlOrderRepository(OrderRepository):
def save(self, order):
db.insert(order)Composition Root
Somewhere outside (for example, in main.py), dependencies are wired together:
def main():
repo = SqlOrderRepository()
use_case = CreateOrderUseCase(order_repo=repo)
controller = OrderController(use_case)This way the use case receives a ready-made dependency instead of creating it itself.
3. Where injection happens
Injection always happens at the outer layer:
- In a web app: in the controller or a DI container (FastAPI, Spring, NestJS).
- In a CLI: at the entry point (
main()). - In tests: through mock objects.
4. Why it matters
- It follows the Dependency Rule (the inner layer does not depend on the outer one).
- It simplifies testing (mocks can be substituted in).
- It lets you easily swap implementations (for example, switching from SQL to NoSQL).
- It makes the architecture extensible and flexible.
Summary:
In Clean Architecture, dependencies are injected from the outside in, through constructors, DI containers, or initialization functions, so that business logic depends only on abstractions, not on concrete implementations.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.