-
Notifications
You must be signed in to change notification settings - Fork 0
DI Container Integration Guide
Status: ✅ Complete
Last Updated: December 15, 2025
This guide explains how to use the RiceCoder Dependency Injection (DI) container (ricecoder-di) for service registration, resolution, and management. The DI container provides a centralized way to wire services across all RiceCoder crates.
- Quick Start
- Service Registration
- Service Lifetimes
- Service Resolution
- Feature-Gated Services
- Conditional Registration
- Health Checks
- Error Handling
- Best Practices
- Examples
use ricecoder_di::create_application_container;
// Create container with core services
let container = create_application_container().unwrap();
// Resolve services
let session_manager = container.resolve::<ricecoder_sessions::SessionManager>().unwrap();
let provider_manager = container.resolve::<ricecoder_providers::provider::manager::ProviderManager>().unwrap();Register a service with a factory function:
use ricecoder_di::{DIContainer, register_service};
let container = DIContainer::new();
// Register with closure
container.register(|_| {
let service = Arc::new(MyService::new());
Ok(service)
}).unwrap();
// Or use the macro
register_service!(container, MyService, |_| Ok(Arc::new(MyService::new())));Services can depend on other services:
container.register(|container| {
let dependency = container.resolve::<DependencyService>()?;
let service = Arc::new(MyService::new(dependency));
Ok(service)
}).unwrap();Created once and reused throughout the application:
container.register(|_| {
Ok(Arc::new(SingletonService::new()))
}).unwrap();Created each time they're requested:
container.register_transient(|_| {
Ok(Arc::new(TransientService::new()))
}).unwrap();Created once per scope and reused within that scope:
use ricecoder_di::ServiceScope;
container.register_scoped(|_| {
Ok(Arc::new(ScopedService::new()))
}).unwrap();
let scope = ServiceScope::new();
let service1 = container.resolve_with_scope::<ScopedService>(Some(&scope)).unwrap();
let service2 = container.resolve_with_scope::<ScopedService>(Some(&scope)).unwrap();
// service1 and service2 are the same instancelet service = container.resolve::<MyService>().unwrap();let scope = ServiceScope::new();
let service = container.resolve_with_scope::<MyService>(Some(&scope)).unwrap();if container.is_registered::<MyService>() {
let service = container.resolve::<MyService>().unwrap();
// Use service
}Services can be conditionally compiled based on Cargo features:
[dependencies]
ricecoder-di = { version = "0.1", features = ["storage", "research"] }| Feature | Services Included |
|---|---|
storage |
StorageManager, FileStorage, MemoryStorage |
research |
ResearchManager, CodebaseScanner, SemanticIndexer |
workflows |
WorkflowEngine, WorkflowManager |
execution |
ExecutionEngine, CommandExecutor |
mcp |
MCPClient, MCPServer |
tools |
ToolRegistry, ToolExecutor |
config |
ConfigManager, ConfigLoader |
activity-log |
ActivityLogger, AuditLogger, SessionTracker |
orchestration |
WorkspaceOrchestrator, OperationManager |
specs |
SpecManager, SpecValidator, SpecCache |
undo-redo |
UndoManager, RedoManager, HistoryManager |
vcs |
VCSManager, GitIntegration, RepositoryManager |
permissions |
PermissionManager, PermissionChecker, AuditLogger |
security |
AccessControl, EncryptionService, ValidationService |
cache |
CacheManager, CacheStorage, CacheStrategy |
domain |
DomainService, Repository, EntityManager |
learning |
LearningManager, PatternCapturer, RuleValidator |
industry |
AuthService, ComplianceManager, ConnectionManager |
safety |
SafetyMonitor, RiskAssessor, ConstraintValidator |
files |
FileManager, FileWatcher, TransactionManager |
themes |
ThemeManager, ThemeLoader, ThemeRegistry |
images |
ImageHandler, ImageAnalyzer, ImageCache |
completion |
GenericCompletionEngine |
lsp |
LSP services |
modes |
ModeManager |
commands |
CommandManager, CommandRegistry |
hooks |
HookRegistry |
keybinds |
KeybindManager |
teams |
TeamManager |
refactoring |
ConfigManager, ProviderRegistry |
full |
All features enabled |
#[cfg(feature = "storage")]
{
let storage_manager = container.resolve::<ricecoder_storage::StorageManager>().unwrap();
// Use storage services
}
#[cfg(feature = "research")]
{
let research_manager = container.resolve::<ricecoder_research::ResearchManager>().unwrap();
// Use research services
}Register services based on runtime conditions:
// Register based on configuration
container.register(|_| {
if config.use_file_storage {
Ok(Arc::new(FileStorage::new(&config.storage_path)))
} else {
Ok(Arc::new(MemoryStorage::new()))
}
}).unwrap();
// Register based on environment
container.register(|_| {
let provider = if std::env::var("USE_OPENAI").is_ok() {
Arc::new(OpenAIProvider::new())
} else {
Arc::new(AnthropicProvider::new())
};
Ok(provider)
}).unwrap();Services can implement health monitoring:
use ricecoder_di::{HealthCheck, HealthStatus};
use async_trait::async_trait;
#[async_trait]
impl HealthCheck for MyService {
async fn health_check(&self) -> DIResult<HealthStatus> {
if self.is_connected() {
Ok(HealthStatus::Healthy)
} else {
Ok(HealthStatus::Unhealthy("Connection lost".to_string()))
}
}
}
// Register with health check
container.register_with_health_check(
|_| Ok(Arc::new(MyService::new())),
|service| async move {
service.health_check().await
}
).unwrap();
// Check all services
let health_results = container.health_check_all().unwrap();
for (service_name, status) in health_results {
match status {
HealthStatus::Healthy => println!("✅ {} is healthy", service_name),
HealthStatus::Degraded(reason) => println!("⚠️ {} is degraded: {}", service_name, reason),
HealthStatus::Unhealthy(reason) => println!("❌ {} is unhealthy: {}", service_name, reason),
}
}Handle common DI errors:
use ricecoder_di::{DIError, DIResult};
match container.resolve::<MyService>() {
Ok(service) => {
// Use service
}
Err(DIError::ServiceNotRegistered { service_type }) => {
eprintln!("Service not registered: {}", service_type);
// Handle missing service
}
Err(DIError::DependencyResolutionFailed { message }) => {
eprintln!("Dependency resolution failed: {}", message);
// Handle dependency issues
}
Err(DIError::CircularDependency { service_chain }) => {
eprintln!("Circular dependency detected: {}", service_chain);
// Handle circular dependencies
}
Err(e) => {
eprintln!("DI error: {}", e);
// Handle other errors
}
}- Singleton: Shared resources, expensive objects, stateless services
- Transient: Lightweight services, per-operation state
- Scoped: Request-scoped data, user-specific services
All services must be Send + Sync:
struct MyService {
data: Arc<RwLock<Data>>, // Use Arc<RwLock<>> for shared mutable state
}Use factory functions for complex initialization:
container.register(|container| {
let config = container.resolve::<ConfigManager>()?;
let storage = container.resolve::<StorageManager>()?;
Ok(Arc::new(MyService::with_dependencies(config, storage)))
}).unwrap();Use feature flags for optional functionality:
#[cfg(feature = "advanced_features")]
container.register(|_| Ok(Arc::new(AdvancedService::new()))).unwrap();Test services in isolation:
#[test]
fn test_service_registration() {
let container = DIContainer::new();
container.register(|_| Ok(Arc::new(TestService::new()))).unwrap();
let service = container.resolve::<TestService>().unwrap();
assert!(service.is_ready());
}use ricecoder_di::{DIContainerBuilder, DIContainerBuilderExt};
fn setup_container() -> DIResult<DIContainer> {
let container = DIContainerBuilder::new()
.register_infrastructure_services()?
.register_use_cases()?
.build()?;
Ok(container)
}fn setup_full_container() -> DIResult<DIContainer> {
let mut builder = DIContainerBuilder::new()
.register_infrastructure_services()?
.register_use_cases()?;
// Add optional services based on features
#[cfg(feature = "storage")]
{ builder = builder.register_storage_services()?; }
#[cfg(feature = "research")]
{ builder = builder.register_research_services()?; }
#[cfg(feature = "workflows")]
{ builder = builder.register_workflow_services()?; }
let container = builder.build()?;
Ok(container)
}struct DatabaseService {
connection_pool: Arc<ConnectionPool>,
}
#[async_trait]
impl HealthCheck for DatabaseService {
async fn health_check(&self) -> DIResult<HealthStatus> {
match self.connection_pool.health_check().await {
Ok(_) => Ok(HealthStatus::Healthy),
Err(e) => Ok(HealthStatus::Unhealthy(e.to_string())),
}
}
}
fn register_database_service(container: &DIContainer) -> DIResult<()> {
container.register_with_health_check(
|_| Ok(Arc::new(DatabaseService::new())),
|service| async move {
service.health_check().await
}
)
}- Architecture Overview - System architecture and DI container role
- API Reference - DI container API documentation
- Service Integration Guide - Adding new services to the DI container
Last updated: December 15, 2025