This guide helps developers migrate from the legacy architecture to the new refactored system with unified services, centralized configuration, and enhanced performance optimizations.
The refactoring introduces several major architectural improvements:
- Unified AI Service: Consolidated all AI operations into a single
AIServiceclass - Centralized Configuration: New
ConfigurationServicefor unified configuration management - Enhanced Caching: Multi-tier caching system with intelligent cache warming
- Centralized Rate Limiting: Unified rate limiting service across all APIs
- Improved Error Handling: Enhanced error categorization and recovery mechanisms
- Optimized Parallel Processing: Enhanced parallel processing with resource management
- Validation Framework: Centralized validation with type safety and error reporting
Before (Legacy):
from services.ai_parser import AIParser
from services.email_generator import EmailGenerator
from services.product_analyzer import ProductAnalyzer
# Multiple service instances
ai_parser = AIParser(config)
email_generator = EmailGenerator(config)
product_analyzer = ProductAnalyzer(config)
# Separate method calls
profile_result = ai_parser.parse_linkedin_profile(html)
product_result = product_analyzer.analyze_product(content)
email_result = email_generator.generate_email(prospect)After (Refactored):
from services.ai_service import AIService, EmailTemplate
# Single unified service
ai_service = AIService(config)
# Unified method calls with consistent response format
profile_result = ai_service.parse_linkedin_profile(html)
product_result = ai_service.analyze_product(content)
email_result = ai_service.generate_email(
prospect=prospect,
template_type=EmailTemplate.COLD_OUTREACH
)Before (Legacy):
from utils.config import Config
# Direct config usage
config = Config.from_env()
service = SomeService(config)After (Refactored):
from utils.configuration_service import ConfigurationService
# Centralized configuration service
config_service = ConfigurationService()
config = config_service.get_config()
service = SomeService(config)
# Or use the service directly
service = SomeService(config_service)Before (Legacy):
from utils.error_handling import handle_error
try:
result = some_operation()
except Exception as e:
handle_error(e, "Operation failed")After (Refactored):
from utils.error_handling_enhanced import EnhancedErrorHandler
error_handler = EnhancedErrorHandler(config)
try:
result = some_operation()
except Exception as e:
error_handler.handle_error(e, context="Operation failed", operation_type="api_call")Before (Legacy):
# No centralized caching
result = expensive_operation()After (Refactored):
from services.caching_service import CachingService
cache_service = CachingService(config)
# Use caching for expensive operations
cache_key = f"operation_{param_hash}"
result = cache_service.get(cache_key)
if result is None:
result = expensive_operation()
cache_service.set(cache_key, result, ttl=3600)Replace legacy imports with new unified services:
# Remove these legacy imports
# from services.ai_parser import AIParser
# from services.email_generator import EmailGenerator
# from services.product_analyzer import ProductAnalyzer
# Add these new imports
from services.ai_service import AIService, EmailTemplate, AIOperationType
from utils.configuration_service import ConfigurationService
from services.caching_service import CachingService
from utils.error_handling_enhanced import EnhancedErrorHandlerLegacy Pattern:
def __init__(self, config):
self.config = config
self.ai_parser = AIParser(config)
self.email_generator = EmailGenerator(config)
self.product_analyzer = ProductAnalyzer(config)Refactored Pattern:
def __init__(self, config_service=None):
self.config_service = config_service or ConfigurationService()
self.config = self.config_service.get_config()
self.ai_service = AIService(self.config)
self.cache_service = CachingService(self.config)
self.error_handler = EnhancedErrorHandler(self.config)Legacy AI Parsing:
# Old way - multiple services
profile_data = self.ai_parser.parse_linkedin_profile(html_content)
product_data = self.product_analyzer.analyze_product(raw_content)
email_content = self.email_generator.generate_email(prospect_data)Refactored AI Operations:
# New way - unified service
profile_result = self.ai_service.parse_linkedin_profile(html_content)
if profile_result.success:
profile_data = profile_result.data
confidence = profile_result.confidence_score
product_result = self.ai_service.analyze_product(raw_content)
if product_result.success:
product_data = product_result.data
email_result = self.ai_service.generate_email(
prospect=prospect_data,
template_type=EmailTemplate.COLD_OUTREACH,
linkedin_profile=profile_data,
product_analysis=product_data
)Legacy Error Handling:
try:
result = risky_operation()
except Exception as e:
logger.error(f"Operation failed: {e}")
return NoneEnhanced Error Handling:
try:
result = risky_operation()
except Exception as e:
error_context = {
'operation': 'risky_operation',
'parameters': {'param1': value1},
'timestamp': datetime.now()
}
recovery_result = self.error_handler.handle_error(
error=e,
context=error_context,
operation_type='api_call',
allow_retry=True
)
if recovery_result.recovered:
result = recovery_result.result
else:
return NoneFor expensive operations, add caching:
def expensive_ai_operation(self, input_data):
# Generate cache key
cache_key = f"ai_operation_{hash(str(input_data))}"
# Try to get from cache first
cached_result = self.cache_service.get(cache_key)
if cached_result is not None:
return cached_result
# Perform expensive operation
result = self.ai_service.some_expensive_operation(input_data)
# Cache the result
if result.success:
self.cache_service.set(cache_key, result, ttl=3600)
return resultAdd new configuration options to your .env file:
# Enhanced AI Service Configuration
AI_SERVICE_CACHE_ENABLED=true
AI_SERVICE_CACHE_TTL=3600
AI_SERVICE_RATE_LIMIT_DELAY=1.0
# Caching Configuration
ENABLE_CACHING=true
CACHE_MEMORY_MAX_ENTRIES=1000
CACHE_MEMORY_MAX_MB=100
CACHE_PERSISTENT_DIR=.cache
CACHE_DEFAULT_TTL=3600
# Enhanced Error Handling
ERROR_HANDLING_ENABLED=true
ERROR_RECOVERY_ATTEMPTS=3
ERROR_NOTIFICATION_ENABLED=true
# Rate Limiting
RATE_LIMITING_ENABLED=true
OPENAI_RATE_LIMIT=60
HUNTER_RATE_LIMIT=100
LINKEDIN_RATE_LIMIT=60Update your configuration validation:
from utils.configuration_service import ConfigurationService
def validate_configuration():
config_service = ConfigurationService()
# Validate all configuration
validation_result = config_service.validate_config()
if not validation_result.is_valid:
print("Configuration validation failed:")
for error in validation_result.errors:
print(f" - {error}")
return False
return True# Update test imports
from services.ai_service import AIService
from utils.configuration_service import ConfigurationService
from services.caching_service import CachingService
from tests.test_utilities import create_test_config, mock_ai_serviceimport pytest
from unittest.mock import Mock, patch
@pytest.fixture
def mock_ai_service():
"""Mock AI service for testing"""
service = Mock(spec=AIService)
service.parse_linkedin_profile.return_value = Mock(
success=True,
data={'name': 'Test User', 'role': 'Developer'},
confidence_score=0.95,
cached=False
)
return service
@pytest.fixture
def test_config_service():
"""Test configuration service"""
return ConfigurationService(config_path="test_config.yaml")# Initialize caching service
cache_service = CachingService(config)
# Use cache warming for frequently accessed data
warming_config = {
"common_profiles": {
"factory": lambda: load_common_linkedin_profiles(),
"ttl": 7200,
"priority": 10
},
"email_templates": {
"factory": lambda: load_email_templates(),
"ttl": 3600,
"priority": 5
}
}
cache_service.warm_cache(warming_config)# Use batch operations when possible
results = []
for item in batch_items:
# Check cache first
cache_key = f"ai_operation_{item.id}"
cached = cache_service.get(cache_key)
if cached:
results.append(cached)
else:
# Process and cache
result = ai_service.process_item(item)
cache_service.set(cache_key, result)
results.append(result)- Import Errors: Update all import statements to use new service locations
- Configuration Errors: Ensure all new environment variables are set
- Cache Issues: Clear cache directory if experiencing cache-related problems
- Rate Limiting: Adjust rate limits if hitting API limits
# Validate new configuration
python -c "from utils.configuration_service import ConfigurationService; ConfigurationService().validate_config()"
# Test AI service
python -c "from services.ai_service import AIService; from utils.config import Config; AIService(Config.from_env())"
# Clear cache
rm -rf .cache/
# Run migration tests
python -m pytest tests/test_migration.py -vIf migration issues occur, you can temporarily rollback by:
- Reverting to legacy import statements
- Using legacy service initialization patterns
- Disabling new features via environment variables:
# Disable new features for rollback
AI_SERVICE_CACHE_ENABLED=false
ENABLE_CACHING=false
ERROR_HANDLING_ENABLED=false
RATE_LIMITING_ENABLED=falseAfter migration, verify:
- All services initialize without errors
- AI operations return consistent results
- Caching is working (check cache hit rates)
- Error handling captures and recovers from failures
- Rate limiting prevents API quota issues
- Configuration validation passes
- All tests pass with new architecture
- Performance metrics show improvements
For migration assistance:
- Check the troubleshooting guide:
docs/TROUBLESHOOTING_GUIDE.md - Run diagnostic commands to identify issues
- Review test examples in
tests/directory - Check service documentation in
docs/directory
The refactored architecture provides significant performance and maintainability improvements while maintaining backward compatibility where possible.