Skip to content

Dependency Injection

santiago edited this page Jan 29, 2026 · 1 revision

Dependency Injection

The framework provides powerful dependency injection capabilities with automatic type resolution.

Constructor Injection

The framework automatically resolves dependencies using Python type annotations. Simply declare your dependencies in the constructor:

class UserService:
    def __init__(self, repository: IUserRepository, logger: ILogger):
        self.repository = repository
        self.logger = logger

The container will automatically:

  1. Look up registered services matching the type annotations
  2. Resolve their dependencies recursively
  3. Inject them into your service

Manual Resolution

You can also manually resolve services from the container:

# Get a service
service = container.get_service(IService)

# The container returns the registered implementation
user_service = container.get_service(IUserService)

Multiple Implementations

If multiple implementations of the same interface are registered, get_service() returns a list:

# If multiple IStrategy implementations are registered
strategies = container.get_service(IStrategy)  # Returns list[IStrategy]

List Injection

You can inject multiple implementations of the same interface by using list[Interface] in your constructor:

class Processor:
    def __init__(self, strategies: list[IStrategy]):
        self.strategies = strategies  # List of all registered IStrategy implementations
        
    def process(self, data):
        for strategy in self.strategies:
            strategy.execute(data)

Register multiple implementations:

@register_service
def register_services(self):
    self.container.add_scoped(IStrategy, lambda: StrategyA())
    self.container.add_scoped(IStrategy, lambda: StrategyB())
    self.container.add_scoped(IStrategy, lambda: StrategyC())
    
    # Processor will receive all three strategies
    self.container.add_scoped(Processor)

Dependency Resolution Flow

  1. Type Annotation: The container reads type annotations from constructor parameters
  2. Service Lookup: It searches for registered services matching the type
  3. Recursive Resolution: If dependencies have their own dependencies, they're resolved recursively
  4. Lifetime Management: The container respects service lifetimes (Singleton, Scoped, Transient)
  5. Injection: Dependencies are injected into the constructor

Example: Complex Dependency Graph

# Register services
@register_service
def register_services(self):
    self.container.add_singleton(IConfiguration, lambda: Configuration())
    self.container.add_scoped(IDatabaseContext, lambda: DatabaseContext(...))
    self.container.add_scoped(IUserRepository, lambda: UserRepository(
        self.container.get_service(IDatabaseContext)
    ))
    self.container.add_scoped(IUserService, lambda: UserService(
        self.container.get_service(IUserRepository),
        self.container.get_service(IConfiguration)
    ))

# Usage in controller
class UserController(BaseController):
    async def get_user(self, req: func.HttpRequest):
        # All dependencies are automatically resolved
        user_service = self.container.get_service(IUserService)
        # user_service has repository, which has database context, etc.

Related Topics

Clone this wiki locally