🚀 Dependency Injection System Roadmap¶
| Field | Value |
|---|---|
| Status | Active |
| Created | 2025-10-01 |
| Updated | 2025-12-22 |
| Author | souperk |
📢 Important Update (2025-12-22)¶
The loryth.common.reflection module has been removed from the codebase. The DI system now uses Python standard library introspection (inspect, typing.get_type_hints(), pkgutil) for all dependency analysis and package scanning. Phase 10 (Reflection Integration) is no longer applicable.
1. 📊 Status Dashboard¶
Overall Progress: 57% Implemented (8/14 phases implemented)
| Phase | Description | Status | TD Reference | Completion |
|---|---|---|---|---|
| Phase 1 | Container API | ✅ Implemented | - | 100% |
| Phase 2 | Bootstrap Runtime | ✅ Implemented | TD-020 | 100% |
| Phase 3 | Injection System | ✅ Implemented | TD-011 | 100% |
| Phase 4 | Enhanced Annotations | ✅ Implemented | TD-012 | 100% |
| Phase 5 | Module Scanning | ✅ Implemented | TD-013 | 100% |
| Phase 6 | Configuration Binding | ✅ Implemented | TD-019 | 100% |
| Phase 7 | Application Entry Point | ✅ Implemented | TD-021 | 100% |
| Phase 8 | Test Resource Cleanup | 📝 Draft | TD-014 | 0% |
| Phase 9 | Type Hints Modularization | ⏳ Planned | - | 0% |
| Phase 10 | Reflection Integration | ❌ Not Applicable | - | N/A |
| Phase 11 | Unified Runtime Marker System | ✅ Implemented | TD-022 | 100% |
| Phase 12 | Test Quality Meta Standards | 📝 Draft | TD-024 | 0% |
| Phase 13 | Unit Test Quality | 📝 Draft | TD-025 | ~10% |
| Phase 14 | Integration Test Quality | 📝 Draft | TD-026 | 0% |
Recent Milestones: - ✅ 2025-10-18: TD-022 completed - Unified runtime marker system successfully implemented (702 tests passing) - ✅ 2025-10-12: TD-021 completed - Application entry point with zero-boilerplate startup - ✅ 2025-10-12: TD-019 completed - Configuration binding with multi-format support - ✅ 2025-10-11: TD-013 completed - Rule-based package scanning replacing processor pattern - ✅ 2025-10-10: TD-020 completed - External bootstrap runtime with idempotent registration - ✅ 2025-10-08: TD-012 completed - Spring Boot-style annotations with lifecycle control - ✅ 2025-10-06: TD-011 completed - Unified injection system with constructor/property/method support
Status Legend: - ✅ Implemented - Implementation finished and tested - 🟢 Accepted - Approved for implementation - 📝 Draft - Design under development/review - ⏳ Planned - Not yet started (no design document)
graph LR
P1[Phase 1<br/>Container API] --> P3[Phase 3<br/>Injection System]
P3 --> P6[Phase 6<br/>Configuration Binding]
P1 --> P2[Phase 2<br/>Bootstrap Runtime]
P1 --> P4[Phase 4<br/>Enhanced Annotations]
P2 --> P5[Phase 5<br/>Module Scanning]
P4 --> P5
P5 --> P7[Phase 7<br/>Application Entry Point]
P13[Phase 13<br/>Unit Test Quality]
P8[Phase 8<br/>Test Resource Cleanup]
P14[Phase 14<br/>Integration Test Quality]
P12[Phase 12<br/>Test Quality Meta]
P14 --> P12
P13 --> P12
P8 --> P12
P12 --> P9[Phase 9<br/>Type Hints Modularization]
P9 --> P10[Phase 10<br/>Reflection Integration]
P11[Phase 11<br/>Runtime Markers] --> P10
classDef implemented fill:none,stroke:#4caf50,stroke-width:3px,color:#4caf50
classDef draft fill:none,stroke:#ff9800,stroke-width:2px,color:#ff9800
classDef planned fill:none,stroke:#9e9e9e,stroke-width:2px,color:#9e9e9e
class P1,P2,P3,P4,P5,P6,P7,P11 implemented
class P8,P12,P13,P14 draft
class P9,P10 planned 2. 📋 Overview¶
This document outlines the comprehensive roadmap for enhancing the dependency injection (DI) system in loryth.common. The roadmap prioritizes DI development to validate and provide feedback on the reflection system, while building a Spring Boot-inspired but Python-native dependency injection framework.
Strategic Goals: 1. Validate Reflection System - Use DI as primary client to exercise and validate reflection capabilities 2. Spring Boot Patterns - Provide familiar annotation-driven development experience 3. Python-Native Design - Embrace Python idioms and constraints (no Java-isms) 4. Zero-Boilerplate - Automatic service discovery and wiring with minimal configuration
3. 🔍 Current State¶
DI System¶
- ✅ Unified Container: Single
DiContainerwith comprehensive registration API - ✅ External Bootstrap:
BootstrapRuntimeorchestrates initialization with plan-based approach - ✅ Idempotent Registration: O(1) duplicate detection with conflict validation
- ✅ Injection System: Constructor, property, and method injection with unified pipeline
- ✅ Enhanced Annotations:
@service,@component,@configuration/@beandecorators - ✅ Module Scanning:
BootstrapPlanBuilderwith rule-based service discovery
Reflection System¶
- Highly modular with swappable components
- Two TypeHintProviders:
SimpleTypeHintProviderandHighFidelityTypeHintProvider - Sophisticated forward reference resolution and caching
- Package scanner with predicate-based filtering
Test Organization¶
- Scattered test resources with unclear naming (
m5_*_pkg/patterns) - Mixed organization between unit/integration/regression tests
- Needs: Feature-based reorganization and consolidation
4. 🛣️ Roadmap Phases¶
Phase 1 (Container API): Container Consolidation & Public API ✅ COMPLETE¶
Objective: Unified container with rich registration and resolution API
Key Capabilities: - Type-safe service registration (singleton, transient, factory, instance) - Generic type resolution with runtime checking: container.resolve[T](type[T]) -> T - Provider-based lazy loading architecture - Thread-safe operations with internal locking
Deliverables: - ✅ Single DiContainer replacing dual implementation approach - ✅ Comprehensive registration API with lifecycle control - ✅ Generic resolution with type safety
Status: Completed during initial DI system development
Phase 2 (Bootstrap): External Bootstrap Runtime ✅ COMPLETE (TD-020)¶
Original Goal: Automatic bootstrapping with manual override capability
What Changed: Bootstrap orchestration was externalized from DiContainer into dedicated BootstrapRuntime to improve separation of concerns, testability, and integration with scanning infrastructure.
Actual Implementation: - Container no longer owns bootstrap logic (removed DiContainer.bootstrap(), auto-bootstrap) - Plan-based bootstrap with type-safe RegistrationStep protocol - Idempotent registration semantics enable safe re-bootstrap scenarios - Comprehensive observability via ContainerMetrics and BootstrapMetrics
Key Deliverables: - ✅ BootstrapRuntime orchestrator with three-phase execution (registration, validation, preparation) - ✅ BootstrapPlan with immutable registration steps - ✅ Container idempotency with O(1) conflict detection via RegistrationKey - ✅ DiContainer.prepare() for explicit eager singleton resolution
Breaking Changes: - Removed: DiContainer.bootstrap(), auto-bootstrap on first resolve, resolve_all() - Changed: Registration APIs now idempotent (duplicate detection) - Added: container.prepare() for fail-fast singleton hydration
Why Changed: - TD-013 scanning requires inspectable plans before bootstrap - Testability improved with external orchestration - Better separation between container (registry) and bootstrap (lifecycle) concerns
Example:
# New pattern - explicit bootstrap workflow
runtime = BootstrapRuntime()
container = DiContainer()
plan = BootstrapPlan(registrations=(
SingletonRegistrationStep(IService, Service),
))
runtime.bootstrap(container, plan)
📖 See: TD-020 External DI Bootstrap Runtime
Phase 3 (Injection): Injection System ✅ COMPLETE (TD-011)¶
Objective: Automatic dependency injection for constructor, property, and method scenarios
Key Deliverables: - ✅ Constructor injection with reflection-based parameter analysis (ConstructorInjector) - ✅ Property injection using Annotated[..., Autowired()] metadata (AutowiredInjector) - ✅ Method wrapping via InjectionProxyFactory for cross-cutting concerns - ✅ Unified injector pipeline with priority-based execution (InjectionManager) - ✅ Comprehensive error handling with InjectorError hierarchy - ✅ Integration with TD-020 bootstrap lifecycle
Remaining Work: - ⏳ Performance benchmarking (moved to TD-018) - ⏳ TD-012 annotation-driven auto-registration integration
Example:
# Constructor injection (automatic)
class UserService:
def __init__(self, repo: UserRepository, logger: Logger):
...
# Property injection
class OrderService:
user_service: Annotated[UserService, Autowired()]
config: Annotated[AppConfig, Autowired()]
# Custom injectors for method wrapping
container = (DiContainerBuilder()
.with_injector(TransactionalInjector())
.with_injector(CachingInjector())
.build())
📖 See: TD-011 DI Injection System (Archived - Complete)
Phase 4 (Annotations): Enhanced DI Annotations ✅ COMPLETE (TD-012)¶
Objective: Spring Boot-style declarative service registration with lifecycle control
Key Deliverables: - ✅ @service annotation with implicit singleton lifecycle - ✅ @component annotation with explicit lifecycle control (@singleton, @transient) - ✅ @configuration classes with @bean factory methods - ✅ Annotation metadata capture for TD-013 scanning compatibility - ✅ DiContainerBuilder.process_annotated_classes() API - ✅ Protocol registration for directly implemented interfaces
Remaining Work: - ⏳ Configuration file integration (YAML/JSON/TOML) - moved to TD-019
Note: TD-013 replaced the AnnotationProcessor pattern with RegistrationRule system. Annotations now feed into scanning infrastructure via built-in rules.
Example:
@service # implicit singleton
class UserService:
...
@component
@transient
class RequestHandler:
...
@configuration
class AppConfig:
@bean
def database_connection(self) -> DatabaseConnection:
...
📖 See: TD-012 Enhanced DI Annotations
Phase 5 (Scanning): Module Scanning & Auto-Registration ✅ COMPLETE (TD-013)¶
Original Goal: Automatic service discovery through package scanning
What Changed: TD-013 replaced the TD-012 AnnotationProcessor pattern (direct container mutation) with composable RegistrationRule system that builds inspectable BootstrapPlan objects compatible with TD-020.
Actual Implementation: - BootstrapPlanBuilder with fluent API for scanning and plan building - RegistrationRule protocol for extensible annotation-to-step translation - Built-in rules for @service, @component, @configuration, @bean - Integration with reflection scanner using predicate-based filtering
Key Deliverables: - ✅ BootstrapPlanBuilder.create() with sensible defaults - ✅ ScanningConfiguration for package filtering - ✅ Union semantics - all matching rules apply to each class - ✅ Step deduplication delegated to TD-020 container idempotency
Breaking Changes: - Removed: AnnotationProcessor, InjectorBridge, AnnotationRegistry - Added: RegistrationRule protocol, BootstrapPlanBuilder fluent API
Why Changed: - TD-020 plan-based bootstrap incompatible with direct container mutation - Inspectable plans enable validation and debugging before bootstrap - Composable rules more extensible than monolithic processor
Example:
builder = BootstrapPlanBuilder.create()
config = ScanningConfiguration(
packages=["myapp.services"],
include_annotations=[ServiceAnnotation],
)
plan = builder.scan_packages(config).build()
runtime = BootstrapRuntime()
container = DiContainer()
runtime.bootstrap(container, plan)
Note: Application entry point (@application decorator) split into separate TD-021 for cleaner separation of concerns.
📖 See: TD-013 Bootstrap Plan Building via Package Scanning
Phase 6 (Config Binding): Configuration Binding & Sources ✅ COMPLETE (TD-019)¶
Objective: Declarative configuration binding via injectors with multi-format support
Key Deliverables: - ✅ Annotated[T, Config("path")] metadata for configuration dependencies - ✅ Integration with TD-011 injector pipeline for property/parameter injection - ✅ Multi-format configuration sources (YAML, JSON, TOML) with deterministic precedence - ✅ Configuration registry with dotted-key resolution (database.host) - ✅ Extensible loader system for custom configuration sources - ✅ Pydantic model support with validation error wrapping - ✅ Immutable deep copies for configuration safety
Example:
from typing import Annotated
from loryth.common.di.configuration import (
BaseConfigResolver,
Config,
ConfigInjector,
ConfigResolver,
ConfigurationRegistry,
PydanticConfigResolver,
)
@service
class DatabaseClient:
host: Annotated[str, Config("database.host")]
port: Annotated[int, Config("database.port")] = 5432
@configuration
class AppConfig:
@bean
def metrics_client(
self,
api_key: Annotated[str, Config("metrics.api_key")],
endpoint: Annotated[str, Config("metrics.endpoint")],
) -> MetricsClient:
return MetricsClient(api_key=api_key, endpoint=endpoint)
container = DiContainer()
registry = ConfigurationRegistry.create()
for source in (
"config/application.yaml",
"config/environment.yaml",
):
registry.load(source)
resolver = PydanticConfigResolver(BaseConfigResolver(registry))
container.register_instance(ConfigResolver, resolver)
container.register_injector(ConfigInjector())
Dependencies: - Requires Phase 3 (injection system) for injector integration - Complements Phase 4 (annotations) - configuration was explicitly deferred from TD-012
Implementation Summary: - Total Tests: 82 (76 unit + 6 integration), all passing - Code Quality: 100% mypy compliance (strict mode), 100% ruff compliance - Full Test Suite: 668 tests passing (no regressions) - Implementation Date: 2025-10-12 - Files Created: 7 source files, 6 test files, 4 test resource files
📖 See: TD-019 DI Configuration Binding & Sources
Phase 7 (App Entry): Application Entry Point & Bootstrap Orchestration 🟢 ACCEPTED (TD-021)¶
Objective: Standardized application entry point with zero-boilerplate startup
Key Goals: - @application decorator for marking application entry point classes - ApplicationRuntime.launch("module:Class") coordinating scan → plan → bootstrap → run workflow - ApplicationBase contract with a single run() lifecycle method fed by constructor/autowired injection - Integration with TD-013 scanning and TD-020 bootstrap runtime metrics - Zero-boilerplate application startup via module path resolution
Example:
@application(scan_packages=["myapp.services", "myapp.repositories"])
class MyApplication(ApplicationBase):
def __init__(self, service: MainService) -> None:
self._service = service
def run(self) -> None:
# Application entry point
self._service.start()
# Zero-boilerplate startup
if __name__ == "__main__":
ApplicationRuntime.launch("myapp.entry:MyApplication")
Dependencies: - Requires Phase 5 (scanning) for automatic service discovery - Builds on TD-020 (bootstrap runtime) for plan execution
Note: Split from TD-013 scope for cleaner separation between scanning infrastructure and application lifecycle orchestration.
📖 See: TD-021 Application Entry Point and Bootstrap Orchestration
Phase 8 (Test Cleanup): Test Resource Organization ⏳ PLANNED (TD-014)¶
Objective: Organize test resources by feature for improved maintainability
Key Goals: - Feature-based organization (reflection/, di/, annotations/) - Consolidate scattered m5_*_pkg test resources - Independent test suites with no shared resources - Clear naming conventions
Target Structure:
tests/resources/
├── reflection/
│ ├── forward_refs/
│ ├── type_hints/
│ └── scanning/
├── di/
│ ├── injection/
│ ├── lifecycle/
│ └── scanning/
└── integration/
└── di_reflection/
📖 See: TD-014 Test Resource Cleanup
Phase 9 (Modularization): Type Hints Provider Modularization ⏳ PLANNED¶
Objective: Extract type hints into submodule for improved readability and maintainability
Key Goals: - Extract to loryth.common.reflection.type_hints submodule - Reduce file sizes and improve navigability - Maintain backward compatibility with existing API
Target Structure:
src/loryth/common/reflection/type_hints/
├── providers/
│ ├── simple_provider.py
│ ├── high_fidelity_provider.py
│ └── composite_provider.py
└── resolution/
├── forward_refs.py
└── annotation_extraction.py
Phase 10 (Integration): Reflection Components & DI Integration ⏳ PLANNED¶
Objective: Establish DI as primary client of reflection system with global context
Key Goals: - Global ReflectionContext with provider extensibility - Thread-safe global state management - DI uses reflection for scanning, annotation inspection, type resolution - Reflection remains independent (DI doesn't register providers)
Example:
from loryth.common.reflection import reflection
# Add custom providers globally
reflection.add_type_hint_provider(CustomProvider())
# DI leverages global reflection context
builder = BootstrapPlanBuilder.create() # uses global reflection
graph LR
subgraph Reflection
RC[ReflectionContext]
Scanner[Scanner]
TypeHints[Type Hints]
end
subgraph DI
Builder[PlanBuilder]
Runtime[BootstrapRuntime]
Container[DiContainer]
end
RC --> Builder
Scanner --> Builder
TypeHints --> Container
Builder --> Runtime
Runtime --> Container
classDef reflection fill:none,stroke:#2196f3,stroke-width:2px,color:#2196f3
classDef di fill:none,stroke:#4caf50,stroke-width:2px,color:#4caf50
class RC,Scanner,TypeHints reflection
class Builder,Runtime,Container di Phase 11 (Markers): Unified Runtime Marker System 🟢 ACCEPTED (TD-022)¶
Objective: Refactor runtime metadata system from loryth.common.annotations to loryth.common.markers with improved terminology and unified discovery
Key Goals: - Eliminate terminology confusion between Python's compile-time annotations and our runtime metadata system - Unified API supporting both callable markers (classes, functions, methods) and attribute markers (via Annotated[]) - Single discovery interface for querying both marker types consistently - Complete DI system migration to new marker infrastructure - Zero compatibility shims - clean module replacement
Example:
from loryth.common.markers import Marker, attach, find_marker
class ServiceMarker(Marker):
def __init__(self, lifecycle: str = "singleton") -> None:
super().__init__(name="service")
self.lifecycle = lifecycle
# Callable marker
@service
class UserRepository:
pass
# Attribute marker
class OrderService:
db: Annotated[DatabaseService, InjectedMarker()]
# Unified discovery
service_marker = find_marker(UserRepository, ServiceMarker)
db_marker = find_marker((OrderService, "db"), InjectedMarker)
Dependencies: - Requires Phase 1-7 completion for understanding current DI architecture - Builds on existing loryth.common.annotations patterns
Implementation Phases: 1. Foundation: Create loryth.common.markers module with core infrastructure 2. Handlers & Context: Implement concrete handlers and global context 3. DI Integration: Migrate all DI decorators and utilities to new module
📖 See: TD-022 Unified Runtime Marker System
5. 📅 Major Changes Changelog¶
This section documents significant course changes and rationale for divergences from the original plan.
TD-020: External Bootstrap Runtime (Phase 2)¶
Date: 2025-10-10 Impact: Breaking change - removed embedded bootstrap from container
What Changed: - Bootstrap orchestration moved from DiContainer to external BootstrapRuntime - Container no longer has bootstrap() method or auto-bootstrap behavior - Registration APIs made idempotent with O(1) duplicate detection - Introduced plan-based bootstrap with type-safe RegistrationStep protocol
Why Changed: 1. Separation of Concerns: Container focuses on registry/resolution, runtime handles lifecycle 2. Testability: Easier to test container without bootstrap side effects 3. TD-013 Integration: Scanning requires inspectable plans before bootstrap execution 4. Idempotency Needs: Safe re-bootstrap required for plugin/module discovery scenarios
Migration Impact: All code using container.bootstrap() must migrate to:
runtime = BootstrapRuntime()
runtime.bootstrap(container, plan)
TD-013: Rule-Based Scanning (Phase 5)¶
Date: 2025-10-11 Impact: Replaced TD-012 AnnotationProcessor pattern
What Changed: - AnnotationProcessor (direct container mutation) replaced with RegistrationRule protocol - Introduced BootstrapPlanBuilder with fluent API for plan composition - Rules produce registration steps instead of directly mutating container - Application entry point (@application) split into separate TD-021
Why Changed: 1. TD-020 Compatibility: Plan-based bootstrap requires inspectable plans, not direct mutation 2. Extensibility: Composable rules more flexible than monolithic processor 3. Debuggability: Plans can be inspected/validated before bootstrap 4. Separation of Concerns: Scanning infrastructure (TD-013) vs application lifecycle (TD-021)
Migration Impact: Code using AnnotationProcessor must migrate to BootstrapPlanBuilder:
# Before (TD-012 processor)
processor.process([UserService, EmailService])
# After (TD-013 rules)
builder.add_class(UserService).add_class(EmailService)
plan = builder.build()
TD-019: Configuration Binding (Phase 6)¶
Date: 2025-10-06 (Draft) Impact: New phase for configuration value injection
What Changed: - Configuration binding explicitly deferred from TD-012 (annotations) to separate TD-019 - Config[T, "path"] using typing.Annotated for declarative configuration dependencies - Configuration injector integrates with TD-011 property/method injection pipeline - Multi-format sources (YAML, JSON, TOML) with pluggable provider system
Why Changed: 1. Separation of Concerns: Annotation metadata (TD-012) separate from configuration binding 2. Injector Integration: Natural fit with TD-011 injector pipeline 3. Extensibility: Pluggable providers enable custom configuration sources
Status: TD-019 currently in Draft status
TD-021: Application Entry Point Split (Phase 7)¶
Date: 2025-10-12 (Accepted) Impact: New phase split from TD-013 scope
What Changed: - Application entry point (@application decorator) moved from TD-013 to separate TD-021 - ApplicationRuntime coordinator for standardized application startup - Lifecycle hooks (configure(), run()) for post-bootstrap customization
Why Changed: 1. Scope Management: TD-013 grew too large with both scanning and application lifecycle 2. Cleaner Design: Scanning infrastructure (TD-013) separate from application orchestration (TD-021) 3. Phased Implementation: Can complete scanning before tackling entry point patterns
Status: TD-021 now Accepted for implementation
6. 🔮 Future Work¶
Advanced Features¶
- Lifecycle Scopes: Request, session, application scopes beyond singleton/transient
- Configuration Integration:
@ConfigValueannotation for property injection from config files - AOP Interceptors: Cross-cutting concerns (logging, security, transactions)
- Named Registrations: Multiple implementations with qualifiers
- Conditional Registration: Environment-based or profile-based service activation
Performance & Observability¶
- Startup Performance: Optimize bootstrap time for large applications
- Memory Profiling: Efficient caching and instance management
- Metrics Dashboard: Real-time DI resolution metrics and performance monitoring
- Dependency Visualization: Graph visualization for debugging complex dependency trees
7. 🎯 Success Criteria¶
Overall Roadmap Success: 1. ✅ Phase 1-5 Complete: Container, bootstrap, injection, annotations, and scanning operational 2. ⏳ Phase 6 Planned: Configuration binding for declarative config injection 3. ⏳ Phase 7 Planned: Application entry point with zero-boilerplate startup 4. ⏳ Phase 8-10 Planned: Organization and integration improvements
Quality Metrics: - Sub-second bootstrap for typical applications (<100 services) - Comprehensive test coverage (unit, integration, regression) - Clear error messages with actionable guidance - Complete user documentation with examples
Reflection Validation: - DI successfully exercises all reflection capabilities (scanning, type hints, annotations) - Feedback loop identifies and resolves reflection system gaps - Reflection system validated as suitable foundation for framework development
8. 🔧 Technical Notes¶
Thread Safety¶
- Container operations thread-safe via internal
RLock - Bootstrap runtime thread-safe through container locking
- Global reflection context requires synchronized provider registration
Idempotency Semantics¶
- Identical registrations are no-ops (O(1) duplicate detection)
- Conflicting registrations raise
DiRegistrationError - Re-bootstrap with same plan produces no observable changes
Python Constraints¶
- Generic type erasure requires explicit type parameters:
resolve[T](type[T]) - Property injection uses
Autowired[T]type hints (not decorator syntax) - Forward reference resolution handled by reflection system
Last Updated: 2025-10-11 Next Review: After TD-021 implementation kickoff
This roadmap prioritizes DI development to validate the reflection system while building a comprehensive dependency injection framework inspired by Spring Boot but designed for Python's unique characteristics and constraints.