title: "Generic Service Implementations Roadmap" description: "Transforming hardcoded core service implementations into generic interfaces with pluggable providers" category: roadmap tags:
- architecture
- refactoring
- dependency-injection
- services
- generic-programming last_updated: March 27, 2025 version: 1.0
Generic Service Implementations Roadmap
Overview
This roadmap outlines the steps to transform hardcoded service implementations in the core module into generic interfaces with pluggable providers. Following the successful pattern of refactoring our auth system from Entra-specific to a generic OAuth implementation, we'll apply the same approach to other core services that are currently hardcoded.
Current Progress
- Phase 1 (Database Service Generalization): 100% Complete
- Phase 2 (Health Service Generalization): 100% Complete
- Phase 3 (Cache Service Generalization): 100% Complete
- Phase 4 (Collection Model Generalization): 100% Complete
- Phase 5 (Logging Service Generalization): 100% Complete
- Overall Progress: 71% (5/7 phases completed)
Current Status
We've identified several hardcoded implementations in the core that should be made generic:
- Database Service: Currently hardcoded to use InMemoryDatabase ✅
- Health Service: Hardcoded health indicators ✅
- Cache Implementation: Specifically tied to Moka cache ✅
- Database Collection Model: Specific methods for user collection ✅
- Logging Service: Direct use of tracing crate ✅
- Database Provider: Only supports in-memory database
Target State
Services in the core module should:
- Be defined through generic traits/interfaces
- Support multiple implementations through providers
- Use dependency injection for wiring
- Allow configuration-based selection of providers
- Support testing through mock implementations
Implementation Progress Tracking
Phase 1: Database Service Generalization
-
Define Database Interface
-
Create
DatabaseInterface
trait to abstract database operations - Define key operations (get, set, delete, query)
- Add generic type parameters for flexible implementation
- Create provider trait for database instantiation
Updated at: March 26, 2025 - Completed implementation of DatabaseOperations and DatabaseProvider traits
-
Create
-
Refactor In-Memory Database
- Make InMemoryDatabase implement the new interface
- Update database service to use the interface
- Create separate implementation module
- Add tests for the implementation
Updated at: March 26, 2025 - Implemented InMemoryDatabase that uses the new interface
-
Implement Configuration System
- Update DatabaseConfig to support provider selection
- Implement provider registry
- Create factory method for database instantiation
- Add configuration validation
Updated at: March 26, 2025 - Created DatabaseProviderRegistry with provider selection and validation
Phase 2: Health Service Generalization
-
Define Health Check Interface
-
Create
HealthIndicator
trait (already exists but needs enhancement) - Add provider system for health indicators
- Create registration mechanism for custom indicators
- Implement discovery mechanism for auto-registration
Updated at: March 26, 2025 - Completed all Health Indicator Interface tasks, including dynamic discovery support with the HealthDiscoveryService
-
Create
-
Refactor Health Indicators
- Move existing indicators to separate modules
- Make all indicators pluggable
- Implement conditional indicators based on config
- Add dynamic health indicator support
Updated at: March 26, 2025 - Completed all Health Indicator refactoring, including dynamic indicator registration and discovery
-
Implement Health Dashboard
- Centralize health data collection
- Add metadata support for indicators
- Implement status aggregation
- Create detailed reporting system
Updated at: March 26, 2025 - Implemented complete Health Dashboard with detailed reporting, history tracking, and dynamic indicator support
Phase 3: Cache Service Generalization
-
Define Cache Interface
-
Create
CacheProvider
trait - Abstract cache operations from implementation
- Support different serialization strategies
- Define eviction policy interface
Updated at: March 26, 2025 - Created comprehensive cache provider interface with support for various eviction policies and serialization strategies
-
Create
-
Refactor Moka Cache Implementation
- Make the existing implementation a provider
- Create separate module for Moka implementation
- Remove direct Moka dependencies from core
- Implement adapter pattern for Moka
Updated at: March 26, 2025 - Replaced direct Moka dependency with a custom implementation that follows the new generic interface
-
Add Alternative Cache Implementation
- Implement simple in-memory cache
- Create Redis cache provider (placeholder)
- Add configuration for selecting providers
- Implement cache provider factory
Updated at: March 26, 2025 - Implemented in-memory cache provider and Redis placeholder with provider factory for cache instantiation
-
Implement Two-Tier Cache Fallback
-
Create
TwoTierCache
implementation - Support fast cache (memory) with slow cache (Redis) fallback
- Add automatic promotion of items from slow to fast cache
- Support configurable TTLs for each cache level
Updated at: March 26, 2025 - Implemented TwoTierCache with fast/slow cache layers, automatic promotion, and configurable TTLs
-
Create
Phase 4: Collection Model Generalization
-
Define Entity Interface
- Create generic entity trait
- Define common CRUD operations
- Implement repository pattern
- Support type-safe collections
Updated at: March 26, 2025 - Completed implementation of Entity and Repository traits, with generic ID type support
-
Refactor User Collection
- Abstract user-specific methods to generic pattern
- Create repository implementations
- Implement type mapping between layers
- Add comprehensive tests
Updated at: March 26, 2025 - Implemented InMemoryRepository and UserService that follows the repository pattern
-
Create Repository Pattern Documentation
- Document repository pattern implementation
- Add examples for custom repositories
- Create migration guide for existing code
- Update architecture documentation
Updated at: March 26, 2025 - Created comprehensive documentation for the repository pattern in docs/examples/repository-pattern-example.md
Phase 5: Logging Service Generalization
-
Define Logging Interface
-
Create
LoggingProvider
trait - Abstract core logging operations
- Support structured logging
- Define log filtering and sampling interfaces
Updated at: March 26, 2025 - Implemented LoggingProvider trait with comprehensive interface for all logging operations
-
Create
-
Refactor Existing Logging Implementation
- Make current logging system a provider
- Create separate module for implementation
- Remove direct logging dependencies from core
- Add adapter pattern for current logger
Updated at: March 26, 2025 - Created TracingLoggerProvider to adapt the existing tracing-based logging to the new interface
-
Add Enterprise Logging Providers
- Create console logging provider
- Add support for structured logging format
- Implement provider registry for swappable implementations
- Support global context and child loggers
Updated at: March 26, 2025 - Implemented ConsoleLoggerProvider with colored output and created LoggingProviderRegistry for dynamic selection
Phase 6: Observability Service Generalization
-
Define Observability Interface
-
Create
ObservabilityProvider
trait - Define metrics, tracing, and profiling operations
- Support context propagation
- Create sampling and filtering mechanisms
Updated at: Not started
-
Create
-
Refactor Metrics Implementation
- Adapt current metrics to provider interface
- Create separate metrics module
- Implement adapter for current metrics
- Add telemetry correlation support
Updated at: Not started
-
Add Enterprise Observability Providers
- Create Dynatrace integration
- Add OpenTelemetry support
- Implement Prometheus metrics provider
- Support distributed tracing with Jaeger
Updated at: Not started
Phase 7: Configuration Service Generalization
-
Define Configuration Interface
-
Create
ConfigProvider
trait - Abstract configuration loading and refreshing
- Support environment-specific configuration
- Add configuration change notifications
Updated at: Not started
-
Create
-
Refactor Static Configuration
- Make current file-based config a provider
- Create separate config modules by domain
- Support hot reloading of configuration
- Add validation rules framework
Updated at: Not started
-
Add Dynamic Configuration Providers
- Implement environment variable provider
- Create AWS Parameter Store/Secrets Manager provider
- Add etcd/Consul KV integration
- Support feature flags and A/B testing config
Updated at: Not started
Implementation Status
- Overall Progress: 71% complete (Phases 1-5 fully completed)
- Last Updated: March 26, 2025
- Next Milestone: Begin Observability Service Generalization (Phase 6)
- Current Focus: Completed implementation of logging service generalization with provider registry and multiple implementations
Success Criteria
- No hardcoded service implementations in the core module
- All services defined by interfaces with at least two implementations
- 100% test coverage of interfaces and 90%+ for implementations
- Comprehensive documentation for extending services
- Migration guide for updating client code
- Performance metrics showing no regression from the refactoring
Detailed Implementation Guide
Step 1: Database Interface Implementation
Start by creating a clear abstraction for database operations:
#![allow(unused)] fn main() { /// Trait defining database operations pub trait DatabaseOperations: Send + Sync { /// Get a value from the database async fn get(&self, collection: &str, key: &str) -> Result<Option<String>, ServiceError>; /// Set a value in the database async fn set(&self, collection: &str, key: &str, value: &str) -> Result<(), ServiceError>; /// Delete a value from the database async fn delete(&self, collection: &str, key: &str) -> Result<bool, ServiceError>; /// Query the database with a filter async fn query(&self, collection: &str, filter: &str) -> Result<Vec<String>, ServiceError>; } /// Trait for database providers #[async_trait] pub trait DatabaseProvider: Send + Sync { /// The type of database this provider creates type Database: DatabaseOperations; /// Create a new database instance async fn create_database(&self, config: DatabaseConfig) -> Result<Self::Database, ServiceError>; /// Check if this provider supports the given configuration fn supports(&self, config: &DatabaseConfig) -> bool; } }
Step 2: Health Indicator Implementation
Enhance the existing health indicator system:
#![allow(unused)] fn main() { /// Extended HealthIndicator trait pub trait HealthIndicator: Send + Sync { /// Get the name of this health indicator fn name(&self) -> String; /// Check the health of this component fn check_health(&self, state: &Arc<AppState>) -> DependencyStatus; /// Get metadata about this indicator fn metadata(&self) -> HashMap<String, String> { HashMap::new() } /// Get the order in which this indicator should be checked fn order(&self) -> i32 { 0 } /// Whether this indicator is critical (failure means system is down) fn is_critical(&self) -> bool { false } } /// Health indicator provider trait pub trait HealthIndicatorProvider: Send + Sync { /// Create health indicators for the application fn create_indicators(&self) -> Vec<Box<dyn HealthIndicator>>; /// Whether this provider is enabled fn is_enabled(&self, config: &AppConfig) -> bool; } }
Step 3: Cache Implementation
Abstract the cache implementation:
#![allow(unused)] fn main() { /// Cache operations trait pub trait CacheOperations<T>: Send + Sync { /// Get a value from the cache async fn get(&self, key: &str) -> Option<T>; /// Set a value in the cache async fn set(&self, key: &str, value: T, ttl: Option<Duration>) -> Result<(), CacheError>; /// Delete a value from the cache async fn delete(&self, key: &str) -> Result<bool, CacheError>; /// Clear the cache async fn clear(&self) -> Result<(), CacheError>; /// Get cache statistics fn stats(&self) -> CacheStats; } /// Cache provider trait #[async_trait] pub trait CacheProvider: Send + Sync { /// Create a new cache instance async fn create_cache<T: Send + Sync + 'static>( &self, config: CacheConfig ) -> Result<Box<dyn CacheOperations<T>>, CacheError>; /// Check if this provider supports the given configuration fn supports(&self, config: &CacheConfig) -> bool; } }
Testing Strategy
For each refactored service:
- Define interface tests that work with any implementation
- Create mock implementations for testing
- Test both success and error paths
- Add integration tests for real-world scenarios
- Benchmark before and after to ensure no performance regression
Example test for database interface:
#![allow(unused)] fn main() { #[tokio::test] async fn test_database_interface() { // Create a mock database let db = MockDatabase::new(); // Set expectations db.expect_get() .with(eq("users"), eq("1")) .returning(|_, _| Ok(Some("Alice".to_string()))); // Test the interface let result = db.get("users", "1").await.unwrap(); assert_eq!(result, Some("Alice".to_string())); } }
Performance Considerations
While making services generic, maintain performance by:
- Using static dispatch where possible
- Avoiding unnecessary boxing
- Minimizing indirection
- Using async properly
- Implementing efficient provider discovery
Packaging and Distribution
To enable easy adoption:
- Create separate crates for provider implementations
- Use feature flags for optional providers
- Provide examples for each implementation
- Include benchmarks in documentation