Phase 2: DI Integration - Breaking Circular Dependencies
Parent Epic: #116
Depends On: #117 (DI Foundation)
Target: Week 2 (7-10 days)
Risk Level: Medium
Migrate core services to DI, eliminating 120-130 circular dependencies and validating clean package boundaries.
Goals
- Migrate Indexer and search services to DI
- Eliminate 75% of circular dependencies (120-130 violations)
- Old pattern still works (coexistence during migration)
- Update tests to use DI overrides
- Validate package boundaries
- No performance regression
- Documentation updated
Why This Phase Matters
This is where the magic happens! By migrating services to DI:
- ✅ Eliminates providers → engine dependency (20 violations)
- ✅ Eliminates telemetry → engine/config dependencies (10 violations)
- ✅ Eliminates config → CLI dependencies (multiple violations)
- ✅ Breaks registry coupling that creates circular imports
- ✅ Enables clean package separation in Phase 3
Impact: From 164 violations → ~30-40 violations (75% reduction)
How DI Breaks Circular Dependencies
Before: Manual Registry Access Creates Coupling
class Indexer:
def __init__(self):
# Creates providers → engine → config → providers cycle
registry = get_provider_registry()
self.embedding = registry.get_embedding_provider_instance(...)
# Direct config import creates coupling
self.settings = get_settings()
Problems:
- Imports from registry, config, providers
- Creates circular dependencies
- Hard to test (manual mocking)
- Scattered instantiation logic
After: DI Injection Breaks Cycles
class Indexer:
def __init__(self, embedding: EmbeddingDep, settings: SettingsDep):
self.embedding = embedding # Injected!
self.settings = settings # Injected!
Benefits:
- ✅ No imports from registry, config, or providers
- ✅ No circular dependencies
- ✅ Easy to test (container.override)
- ✅ Clean package boundaries
Implementation Checklist
Service Migration (Days 1-4)
Priority 1: Indexer (Eliminates most violations)
Priority 2: Search Services
Priority 3: Chunking Service
Priority 4: Server Initialization
Example: Indexer Migration
Before:
class Indexer:
def __init__(self):
from codeweaver.common.registry import get_provider_registry
from codeweaver.config.settings import get_settings
registry = get_provider_registry()
self.embedding = registry.get_embedding_provider_instance(...)
self.vector_store = registry.get_vector_store_instance(...)
self.settings = get_settings()
# Circular dependencies created!
After:
from codeweaver.di.providers import EmbeddingDep, VectorStoreDep, SettingsDep
class Indexer:
def __init__(
self,
embedding: EmbeddingDep,
vector_store: VectorStoreDep,
settings: SettingsDep,
):
self.embedding = embedding
self.vector_store = vector_store
self.settings = settings
# No imports from registry/config!
# No circular dependencies!
Testing Updates (Days 5-6)
Migrate to DI overrides:
Example test transformation:
# Before: Manual mocking
def test_indexer():
indexer = Indexer()
indexer.embedding = MockEmbedding() # Fragile!
indexer.vector_store = MockVectorStore()
# After: Clean DI overrides
def test_indexer(container):
container.override(EmbeddingProvider, MockEmbedding())
container.override(VectorStore, MockVectorStore())
indexer = container.resolve(Indexer) # Clean!
Package Boundary Validation (Days 7-8)
Validate clean dependencies:
Natural boundaries should emerge:
- Services declare dependencies via types
- No manual imports of registry/config
- Clean dependency flow: core → utils → providers → engine → app
Performance Testing (Day 9)
Benchmark DI overhead:
Performance mitigation:
- Singleton caching (already in DI)
- Lazy resolution where appropriate
- Pre-warm frequently used dependencies
Documentation (Day 10)
Validation Checklist
Dependency Analysis:
Functional Testing:
Acceptance Criteria
Coexistence Strategy
During migration:
- DI services can coexist with old pattern
- Gradual migration, service by service
- No big-bang switchover
- Rollback possible at any point
Example coexistence:
# Old pattern still works
indexer_old = Indexer()
# New pattern also works
container = get_container()
indexer_new = container.resolve(Indexer)
Violations Eliminated
Before: 164 Total Violations
Category 1: Registry/Config Access (101)
- codeweaver → core (68)
- codeweaver → utils (23)
- codeweaver → telemetry (10)
Category 2: Provider Coupling (24)
- providers → engine (20) ← ELIMINATED
- providers → agent_api (4) ← Partially fixed by SearchResult move
Category 3: Telemetry Coupling (10)
- telemetry → config (3) ← ELIMINATED
- telemetry → engine (3) ← ELIMINATED
- telemetry → utils (3) ← ELIMINATED
- telemetry → semantic (1)
Category 4: Engine Coupling (5)
- engine → CLI (5) ← ELIMINATED
After DI: ~30-40 Violations Remaining
Still need manual fixes:
- core → utils (9) - Move utilities to core
- semantic → utils (4) - Move utilities
- providers → agent_api (4) - Fixed by SearchResult move in Phase 1
- Minor type movements (10-15)
Success Metrics
Technical:
- 75% reduction in violations (164 → ~40)
- 0 test failures
- Performance within 5%
- Clean package boundaries
Quality:
- 50-80% less verbose test setup
- Services declare clean dependencies
- No circular imports between major modules
- FastAPI-familiar patterns
Next Phase Preview
Phase 3 (Monorepo Structure) becomes trivial because:
- ✅ Circular dependencies broken
- ✅ Services don't import across packages
- ✅ Clean dependency flow established
- ✅ Just need to organize into packages/
Connection to Monorepo Strategy
This phase implements Week 2 of the integrated strategy:
- DI Integration → Breaks 75% of circular dependencies
- Package boundaries validated → Ready for Phase 3 organization
- Testing updated → Proof that architecture is sound
Reference
- Planning:
INTEGRATED_DI_MONOREPO_STRATEGY.md (Week 2)
- Architecture:
plans/dependency-injection-architecture-plan.md
- Visualization:
DI_IMPACT_VISUALIZATION.md
Risk Mitigation
Medium Risk: Breaking existing code
- Mitigation: Coexistence strategy, gradual migration
- Rollback: Old pattern still works throughout
Medium Risk: Performance impact
- Mitigation: Early benchmarking, singleton caching
- Target: Within 5% of baseline
Low Risk: Learning curve
- Mitigation: Clear docs, FastAPI familiarity, examples
Phase 2: DI Integration - Breaking Circular Dependencies
Parent Epic: #116
Depends On: #117 (DI Foundation)
Target: Week 2 (7-10 days)
Risk Level: Medium
Migrate core services to DI, eliminating 120-130 circular dependencies and validating clean package boundaries.
Goals
Why This Phase Matters
This is where the magic happens! By migrating services to DI:
Impact: From 164 violations → ~30-40 violations (75% reduction)
How DI Breaks Circular Dependencies
Before: Manual Registry Access Creates Coupling
Problems:
After: DI Injection Breaks Cycles
Benefits:
Implementation Checklist
Service Migration (Days 1-4)
Priority 1: Indexer (Eliminates most violations)
embedding: EmbeddingDepvector_store: VectorStoreDepsettings: SettingsDepPriority 2: Search Services
SemanticSearchServiceHybridSearchServicePriority 3: Chunking Service
Priority 4: Server Initialization
Example: Indexer Migration
Before:
After:
Testing Updates (Days 5-6)
Migrate to DI overrides:
Example test transformation:
Package Boundary Validation (Days 7-8)
Validate clean dependencies:
Natural boundaries should emerge:
Performance Testing (Day 9)
Benchmark DI overhead:
Performance mitigation:
Documentation (Day 10)
Validation Checklist
Dependency Analysis:
python scripts/validate_proposed_structure.pyFunctional Testing:
Acceptance Criteria
Coexistence Strategy
During migration:
Example coexistence:
Violations Eliminated
Before: 164 Total Violations
Category 1: Registry/Config Access (101)
Category 2: Provider Coupling (24)
Category 3: Telemetry Coupling (10)
Category 4: Engine Coupling (5)
After DI: ~30-40 Violations Remaining
Still need manual fixes:
Success Metrics
Technical:
Quality:
Next Phase Preview
Phase 3 (Monorepo Structure) becomes trivial because:
Connection to Monorepo Strategy
This phase implements Week 2 of the integrated strategy:
Reference
INTEGRATED_DI_MONOREPO_STRATEGY.md(Week 2)plans/dependency-injection-architecture-plan.mdDI_IMPACT_VISUALIZATION.mdRisk Mitigation
Medium Risk: Breaking existing code
Medium Risk: Performance impact
Low Risk: Learning curve