implement-dependency-injection
Original:🇺🇸 English
Translated
4 scriptsChecked / no sensitive code detected
Provides comprehensive guide for adding services to dependency injection Container using dependency-injector library patterns including Singleton vs Factory vs Dependency providers, override patterns for testing, and circular dependency detection. Use when creating new service, adding dependency to Container, debugging circular dependency errors, or wiring components for injection.
2installs
Added on
NPX Install
npx skill4agent add dawiddutoit/custom-claude implement-dependency-injectionTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Works with container.py and container_factory.py files.
Implement Dependency Injection
Purpose
Guides proper integration of services into the dependency injection Container using the library. Ensures correct provider selection, prevents circular dependencies, and enables testability through override patterns.
dependency-injectorWhen to Use
Use this skill when:
- Creating new service - Adding a service that needs dependencies
- Adding dependency to Container - Wiring components for injection
- Debugging circular dependency errors - Resolving dependency cycles
- Wiring components for injection - Setting up proper dependency flow
- Setting up test overrides - Mocking dependencies in tests
Trigger phrases:
- "Add X to the dependency container"
- "Wire Y service with dependencies"
- "Fix circular dependency error"
- "Override dependency for testing"
Quick Start
Adding a simple service to Container:
python
# In container.py
my_service = providers.Singleton(
MyService,
settings=settings,
dependency1=some_dependency,
)Table of Contents
Core Sections
- Purpose - Single-sentence skill objective
- Quick Start - Immediate, copy-paste ready example
- Instructions - Complete implementation guide
- Step 1: Determine Provider Type - Singleton vs Factory vs Dependency
- Step 2: Add Service to Container - Container.py integration
- Step 3: Handle Special Cases - Factory functions, config extraction, circular dependencies
- Step 4: Override for Testing - Test fixture patterns
- Step 5: Initialize in Factory - MCP server initialization
- Examples - Concrete, working implementations
- Requirements - Dependencies, files, quality checks
- Common Pitfalls - Troubleshooting guide
Supporting Resources
- references/provider-reference.md - Complete dependency-injector API reference
- references/circular-dependency-guide.md - Circular dependency resolution strategies
- templates/service-template.py - Service class template with DI
Utility Scripts
- Detect Circular Dependencies - Detect circular dependencies in DI container
- Generate Provider - Auto-generate provider code for container
- Validate DI Patterns - Validate dependency injection patterns in codebase
Instructions
Step 1: Determine Provider Type
Choose the appropriate provider based on lifecycle requirements:
Use :
providers.Singleton- Stateful services (repositories, caches)
- Expensive initialization (database connections, embedding models)
- Shared state needed across requests
- Most application/infrastructure services
Use :
providers.Factory- Request handlers (command/query handlers)
- Stateless operations
- New instance needed per invocation
- Short-lived objects
Use :
providers.Dependency- External dependencies provided at runtime
- Configuration objects (Settings)
- Infrastructure connections (neo4j_driver)
- Services from outside the container
Step 2: Add Service to Container
Location:
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container.pyAdd provider in the appropriate layer section:
python
class Container(containers.DeclarativeContainer):
# Configuration
settings = providers.Dependency(instance_of=Settings)
# Infrastructure Layer - Repositories
my_repository = providers.Singleton(
MyRepository,
driver=neo4j_driver,
settings=settings,
)
# Application Layer - Services
my_service = providers.Singleton(
MyService,
settings=settings,
repository=my_repository,
)
# Application Layer - Command Handlers
my_handler = providers.Factory(
MyHandler,
service=my_service,
)Ordering matters: Define dependencies before dependents to avoid circular references.
Step 3: Handle Special Cases
Factory Functions (for complex initialization):
python
def create_extractor_registry(python_ext):
"""Factory function to create and configure extractor registry."""
registry = ExtractorRegistry()
registry.register(python_ext)
return registry
extractor_registry = providers.Singleton(
create_extractor_registry,
python_ext=python_extractor,
)Extracting Config Values:
python
smart_chunking_service = providers.Singleton(
SmartChunkingService,
boundary_adjustment_lines=providers.Factory(
lambda s: s.chunking.boundary_adjustment_lines,
s=settings,
),
settings=settings,
)Avoiding Circular Dependencies:
- Order providers: dependencies before dependents
- Use lazy evaluation with
providers.Factory - Consider extracting shared dependencies to separate provider
- See for resolution strategies
references/circular-dependency-guide.md
Step 4: Override for Testing
In test files (conftest.py or test methods):
python
@pytest.fixture
def container(real_settings, mock_driver):
"""Create container with test overrides."""
container = Container()
# Override dependencies
container.settings.override(real_settings)
container.neo4j_driver.override(mock_driver)
return container
def test_service(container):
"""Test service from container."""
service = container.my_service()
assert service.settings is not NoneStep 5: Initialize in Factory
For MCP server initialization:
Update :
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container_factory.pypython
def initialize_container_and_services(
repository_monitor: RepositoryMonitor,
neo4j_rag: Neo4jRAG,
settings: Settings,
) -> Container:
"""Initialize container with runtime dependencies."""
container = Container()
# Override external dependencies
container.settings.override(settings)
container.neo4j_driver.override(neo4j_rag.neo4j_database.driver)
container.repository_monitor.override(repository_monitor)
# Initialize services with side effects
_ = container.my_service()
return containerExamples
Example 1: Add Repository (Singleton)
python
# In container.py - Infrastructure Layer section
file_metadata_repository = providers.Singleton(
FileMetadataRepository,
driver=neo4j_driver,
settings=settings,
)Why Singleton: Repositories are stateful, manage connections, expensive to create.
Example 2: Add Command Handler (Factory)
python
# In container.py - Application Layer section
process_file_handler = providers.Factory(
ProcessFileHandler,
repository=file_metadata_repository,
chunking_service=chunking_service,
settings=settings,
)Why Factory: Handlers are invoked per request, should be stateless.
Example 3: Override for Testing
python
# In tests/integration/mymodule/conftest.py
@pytest.fixture
def container(real_settings):
container = Container()
container.settings.override(real_settings)
# Override with mock driver
mock_driver = AsyncMock()
container.neo4j_driver.override(mock_driver)
return container
def test_handler_uses_repository(container):
handler = container.process_file_handler()
# handler now uses mocked driver through repositoryRequirements
Dependencies
bash
uv pip install dependency-injectorProject Files
- Container definition:
src/project_watch_mcp/interfaces/mcp/container.py - Factory initialization:
src/project_watch_mcp/interfaces/mcp/container_factory.py - Test examples:
tests/integration/container/test_settings_injection.py
Quality Checks
After adding to container:
bash
# Type checking (ensures proper wiring)
uv run pyright src/project_watch_mcp/interfaces/mcp/container.py
# Run tests
uv run pytest tests/integration/container/ -vCommon Pitfalls
1. Circular Dependency Error
Symptom:
Error while resolving dependenciesSolution: Reorder providers or use factory functions. See .
references/circular-dependency-guide.md2. Wrong Provider Type
Symptom: State leaking between tests or requests
Solution: Use Factory for handlers, Singleton for services/repositories.
3. Missing Override in Tests
Symptom: Tests fail with "Dependency not set"
Solution: Override all Dependency providers in test fixtures.
4. Optional Dependencies
Symptom:
def __init__(self, settings: Settings | None = None)Solution: NEVER make Settings/Config optional. Use and override in tests.
providers.Dependency(instance_of=Settings)See Also
- references/provider-reference.md - Complete dependency-injector API reference
- references/circular-dependency-guide.md - Circular dependency resolution strategies
- templates/service-template.py - Service class template with DI
- scripts/detect_circular_deps.py - Detect circular dependencies utility
- scripts/generate_provider.py - Generate provider code utility
- scripts/validate_di_patterns.py - Validate DI patterns utility