# Service Integration Guide **Status**: ✅ Complete **Last Updated**: December 15, 2025 --- ## Overview This guide is for crate maintainers who need to add new services to the RiceCoder DI container. It covers the process of registering services, handling dependencies, and ensuring proper integration with the existing service ecosystem. ## Table of Contents - [Adding a New Service](#adding-a-new-service) - [Service Dependencies](#service-dependencies) - [Feature Gates](#feature-gates) - [Health Checks](#health-checks) - [Testing Integration](#testing-integration) - [Migration Guide](#migration-guide) - [Examples](#examples) --- ## Adding a New Service ### Step 1: Define the Service Create your service struct that implements `Send + Sync`: ```rust use std::sync::Arc; #[derive(Clone)] pub struct MyNewService { config: Arc, storage: Arc, } impl MyNewService { pub fn new(config: Arc, storage: Arc) -> Self { Self { config, storage } } pub fn do_something(&self) -> Result<(), Error> { // Service implementation Ok(()) } } ``` ### Step 2: Add Service Registration Function Add a registration function to `ricecoder-di/src/services.rs`: ```rust /// Register MyNewService pub fn register_my_new_service(container: &DIContainer) -> DIResult<()> { container.register(|container| { let config = container.resolve::()?; let storage = container.resolve::()?; Ok(Arc::new(MyNewService::new(config, storage))) })?; Ok(()) } ``` ### Step 3: Add to Infrastructure Services Update the `register_infrastructure_services` function: ```rust pub fn register_infrastructure_services(container: &DIContainer) -> DIResult<()> { // ... existing registrations ... // Add your new service register_my_new_service(container)?; Ok(()) } ``` ### Step 4: Add Builder Method (Optional) Add a convenience method to `DIContainerBuilderExt`: ```rust impl DIContainerBuilderExt for DIContainerBuilder { // ... existing methods ... /// Register MyNewService fn register_my_new_service(self) -> DIResult { register_my_new_service(&self.container)?; Ok(self) } } ``` ### Step 5: Update Feature Gates (if needed) If your service should be feature-gated, add it to the appropriate feature section: ```rust #[cfg(feature = "my_feature")] pub fn register_my_new_service(container: &DIContainer) -> DIResult<()> { // Implementation } ``` And update the builder extension: ```rust #[cfg(feature = "my_feature")] fn register_my_new_service(self) -> DIResult { register_my_new_service(&self.container)?; Ok(self) } ``` ## Service Dependencies ### Resolving Dependencies Services can depend on other services through the container: ```rust container.register(|container| { let config = container.resolve::()?; let storage = container.resolve::()?; let logger = container.resolve::()?; Ok(Arc::new(MyService::new(config, storage, logger))) })?; ``` ### Interface Dependencies Use trait objects for flexible dependencies: ```rust pub trait Storage: Send + Sync { async fn save(&self, key: &str, data: &[u8]) -> Result<()>; async fn load(&self, key: &str) -> Result>; } container.register(|container| { let storage: Arc = container.resolve()?; Ok(Arc::new(MyService::new(storage))) })?; ``` ### Optional Dependencies Handle optional dependencies gracefully: ```rust container.register(|container| { let base_service = Arc::new(BaseService::new()); // Add optional features if available #[cfg(feature = "advanced")] let base_service = { let advanced_feature = container.resolve::()?; base_service.with_advanced(advanced_feature) }; Ok(base_service) })?; ``` ## Feature Gates ### Adding a New Feature 1. **Update Cargo.toml features** in `ricecoder-di/Cargo.toml`: ```toml [features] my_feature = ["dep:ricecoder-my-crate"] ``` 2. **Add feature-gated registration**: ```rust #[cfg(feature = "my_feature")] pub fn register_my_feature_services(container: &DIContainer) -> DIResult<()> { container.register(|_| Ok(Arc::new(MyFeatureService::new())))?; Ok(()) } ``` 3. **Update infrastructure registration**: ```rust #[cfg(feature = "my_feature")] register_my_feature_services(container)?; ``` 4. **Add builder method**: ```rust #[cfg(feature = "my_feature")] fn register_my_feature_services(self) -> DIResult { register_my_feature_services(&self.container)?; Ok(self) } ``` 5. **Update usage documentation** in `src/usage.rs`. ### Feature Matrix Update the feature matrix in documentation: ```rust //! - `my_feature` - My new feature services ``` ## Health Checks ### Implementing Health Checks Make your service implement the `HealthCheck` trait: ```rust use ricecoder_di::{HealthCheck, HealthStatus}; use async_trait::async_trait; #[async_trait] impl HealthCheck for MyService { async fn health_check(&self) -> DIResult { // Perform health check if self.is_connected().await { Ok(HealthStatus::Healthy) } else { Ok(HealthStatus::Unhealthy("Connection failed".to_string())) } } } ``` ### Registering with Health Checks ```rust container.register_with_health_check( |_| Ok(Arc::new(MyService::new())), |service| async move { service.health_check().await } )?; ``` ## Testing Integration ### Unit Tests Test service registration and resolution: ```rust #[cfg(test)] mod tests { use super::*; use ricecoder_di::DIContainer; #[test] fn test_my_service_registration() { let container = DIContainer::new(); register_my_new_service(&container).unwrap(); let service = container.resolve::().unwrap(); assert!(service.do_something().is_ok()); } } ``` ### Integration Tests Test with other services: ```rust #[test] fn test_service_integration() { let container = create_application_container().unwrap(); // Should be able to resolve all services let my_service = container.resolve::().unwrap(); let config = container.resolve::().unwrap(); // Test interaction assert!(my_service.uses_config(&config)); } ``` ### Property-Based Tests Test service properties: ```rust #[test] fn test_service_properties() { proptest!(|(input in ".*")| { let container = DIContainer::new(); register_my_new_service(&container).unwrap(); let service = container.resolve::().unwrap(); // Test deterministic behavior let result1 = service.process(&input); let result2 = service.process(&input); prop_assert_eq!(result1, result2); }); } ``` ## Migration Guide ### Migrating Existing Services If you're migrating an existing service to use DI: 1. **Wrap existing service** in an `Arc`: ```rust // Before pub struct MyService { /* fields */ } // After pub type MyService = Arc; struct InnerMyService { /* fields */ } ``` 2. **Update constructors** to work with DI: ```rust // Before impl MyService { pub fn new(config: Config) -> Self { Self { config } } } // After impl MyService { pub fn new(config: Arc) -> Self { Arc::new(InnerMyService { config }) } } ``` 3. **Update all usage sites** to resolve from container instead of direct construction. ### Backward Compatibility Maintain backward compatibility during migration: ```rust impl MyService { // Keep old constructor for backward compatibility pub fn from_config(config: Config) -> Self { Self::new(Arc::new(config)) } // New DI-friendly constructor pub fn new(config: Arc) -> Self { Arc::new(InnerMyService { config }) } } ``` ## Examples ### Complete Service Integration ```rust // In your crate's lib.rs pub mod services; pub mod models; // In services.rs use ricecoder_di::{DIContainer, DIResult}; use std::sync::Arc; pub struct DocumentService { storage: Arc, validator: Arc, } impl DocumentService { pub fn new(storage: Arc, validator: Arc) -> Arc { Arc::new(Self { storage, validator }) } } // Registration function pub fn register_document_services(container: &DIContainer) -> DIResult<()> { // Register dependencies first container.register(|_| Ok(Arc::new(DocumentValidator::new())))?; // Register main service container.register(|container| { let storage = container.resolve::()?; let validator = container.resolve::()?; Ok(DocumentService::new(storage, validator)) })?; Ok(()) } // In ricecoder-di/src/services.rs #[cfg(feature = "documents")] pub fn register_document_services(container: &DIContainer) -> DIResult<()> { ricecoder_documents::services::register_document_services(container) } // Builder extension #[cfg(feature = "documents")] impl DIContainerBuilderExt for DIContainerBuilder { fn register_document_services(self) -> DIResult { register_document_services(&self.container)?; Ok(self) } } ``` ### Service with Health Checks ```rust use ricecoder_di::{HealthCheck, HealthStatus}; use async_trait::async_trait; pub struct DatabaseService { pool: Arc, } #[async_trait] impl HealthCheck for DatabaseService { async fn health_check(&self) -> DIResult { match self.pool.test_connection().await { Ok(_) => Ok(HealthStatus::Healthy), Err(e) => Ok(HealthStatus::Unhealthy(e.to_string())), } } } pub fn register_database_service(container: &DIContainer) -> DIResult<()> { container.register_with_health_check( |_| Ok(Arc::new(DatabaseService::new())), |service| async move { service.health_check().await } ) } ``` ### Feature-Gated Service ```rust // In Cargo.toml [features] ml = ["dep:ricecoder-ml"] // In services.rs #[cfg(feature = "ml")] pub fn register_ml_services(container: &DIContainer) -> DIResult<()> { container.register(|_| Ok(Arc::new(MLService::new())))?; Ok(()) } // In ricecoder-di/src/services.rs #[cfg(feature = "ml")] register_ml_services(container)?; // Builder #[cfg(feature = "ml")] fn register_ml_services(self) -> DIResult { register_ml_services(&self.container)?; Ok(self) } ``` --- ## See Also - [DI Container Integration Guide](./DI-Container-Integration-Guide.md) - Using the DI container - [Architecture Overview](./Architecture-Overview.md) - System architecture - [API Reference](./API-Reference.md) - Complete API documentation --- *Last updated: December 15, 2025*