""" Guide de migration vers la nouvelle architecture avec services découplés. Ce fichier montre comment migrer progressivement du code existant vers la nouvelle architecture avec injection de dépendances. """ from typing import Dict, Any # =================== AVANT : Code couplé avec imports circulaires =================== class OldRoute: """Exemple de l'ancienne approche avec couplage fort.""" def assessment_detail_old(self, assessment_id: int): """Ancienne version avec logique dans les modèles.""" from models import Assessment # Import direct assessment = Assessment.query.get_or_404(assessment_id) # ❌ Problèmes : # 1. Logique métier dans le modèle (violation SRP) # 2. Import circulaire dans grading_progress # 3. Requêtes N+1 dans calculate_student_scores # 4. Pas de testabilité (dépendances hard-codées) progress = assessment.grading_progress # Import circulaire caché scores, exercises = assessment.calculate_student_scores() # N+1 queries stats = assessment.get_assessment_statistics() return { 'assessment': assessment, 'progress': progress, 'scores': scores, 'statistics': stats } # =================== APRÈS : Architecture découplée =================== class NewRoute: """Nouvelle approche avec injection de dépendances.""" def __init__(self, assessment_services_facade=None): """Injection de dépendances pour testabilité.""" if assessment_services_facade is None: from providers.concrete_providers import AssessmentServicesFactory assessment_services_facade = AssessmentServicesFactory.create_facade() self.services = assessment_services_facade def assessment_detail_new(self, assessment_id: int) -> Dict[str, Any]: """ Nouvelle version avec services découplés. ✅ Avantages : 1. Services dédiés (respect SRP) 2. Plus d'imports circulaires 3. Requêtes optimisées (plus de N+1) 4. Testable avec mocks 5. Extensible (pattern Strategy) """ from models_refactored import Assessment # Modèle allégé assessment = Assessment.query.get_or_404(assessment_id) # Appels optimisés aux services progress = self.services.get_grading_progress(assessment) scores, exercises = self.services.calculate_student_scores(assessment) stats = self.services.get_statistics(assessment) return { 'assessment': assessment, 'progress': progress.__dict__, # Conversion DTO -> dict 'scores': {k: v.__dict__ for k, v in scores.items()}, 'statistics': stats.__dict__ } # =================== MIGRATION PROGRESSIVE =================== class MigrationRoute: """Exemple de migration progressive pour minimiser les risques.""" def __init__(self): # Feature flag pour basculer entre ancien et nouveau code self.use_new_services = self._get_feature_flag('USE_NEW_ASSESSMENT_SERVICES') if self.use_new_services: from providers.concrete_providers import AssessmentServicesFactory self.services = AssessmentServicesFactory.create_facade() def assessment_detail_hybrid(self, assessment_id: int): """Version hybride permettant de tester graduellement.""" from models import Assessment # Import de l'ancien modèle assessment = Assessment.query.get_or_404(assessment_id) if self.use_new_services: # Nouvelle implémentation progress = self.services.get_grading_progress(assessment) scores, exercises = self.services.calculate_student_scores(assessment) stats = self.services.get_statistics(assessment) return { 'assessment': assessment, 'progress': progress.__dict__, 'scores': scores, 'statistics': stats.__dict__ } else: # Ancienne implémentation (fallback) progress = assessment.grading_progress scores, exercises = assessment.calculate_student_scores() stats = assessment.get_assessment_statistics() return { 'assessment': assessment, 'progress': progress, 'scores': scores, 'statistics': stats } def _get_feature_flag(self, flag_name: str) -> bool: """Récupère un feature flag depuis la configuration.""" # Exemple d'implémentation import os return os.environ.get(flag_name, 'false').lower() == 'true' # =================== TESTS AVEC LA NOUVELLE ARCHITECTURE =================== class TestableRoute: """Exemple montrant la testabilité améliorée.""" def __init__(self, services_facade): self.services = services_facade def get_assessment_summary(self, assessment_id: int): """Méthode facilement testable avec mocks.""" from models_refactored import Assessment assessment = Assessment.query.get_or_404(assessment_id) progress = self.services.get_grading_progress(assessment) return { 'title': assessment.title, 'progress_percentage': progress.percentage, 'status': progress.status } def test_assessment_summary(): """Test unitaire simple grâce à l'injection de dépendances.""" from unittest.mock import Mock from services.assessment_services import ProgressResult # Création des mocks mock_services = Mock() mock_services.get_grading_progress.return_value = ProgressResult( percentage=75, completed=15, total=20, status='in_progress', students_count=25 ) # Test de la route avec mock injecté route = TestableRoute(mock_services) # Mock de l'assessment mock_assessment = Mock() mock_assessment.title = 'Test Assessment' # Simulation du test (en vrai on moquerait aussi la DB) with patch('models_refactored.Assessment') as mock_model: mock_model.query.get_or_404.return_value = mock_assessment result = route.get_assessment_summary(1) assert result['title'] == 'Test Assessment' assert result['progress_percentage'] == 75 assert result['status'] == 'in_progress' # =================== EXTENSIBILITÉ : Nouveaux types de notation =================== class CustomGradingStrategy: """Exemple d'extension pour un nouveau type de notation.""" def calculate_score(self, grade_value: str, max_points: float) -> float: """Logique personnalisée (ex: notation par lettres A,B,C,D).""" letter_to_score = { 'A': 1.0, 'B': 0.75, 'C': 0.5, 'D': 0.25, 'F': 0.0 } letter = grade_value.upper() ratio = letter_to_score.get(letter, 0.0) return ratio * max_points def get_grading_type(self) -> str: return 'letters' def register_custom_grading(): """Exemple d'enregistrement d'un nouveau type de notation.""" from services.assessment_services import GradingStrategyFactory GradingStrategyFactory.register_strategy('letters', CustomGradingStrategy) # Maintenant le système peut gérer le type 'letters' automatiquement strategy = GradingStrategyFactory.create('letters') score = strategy.calculate_score('B', 20.0) # = 15.0 # =================== MONITORING ET MÉTRIQUES =================== class MonitoredAssessmentService: """Exemple d'ajout de monitoring sans modifier la logique métier.""" def __init__(self, services_facade): self.services = services_facade self.metrics_collector = self._init_metrics() def get_grading_progress_with_metrics(self, assessment): """Wrapper avec métriques autour du service.""" start_time = time.time() try: result = self.services.get_grading_progress(assessment) # Métriques de succès self.metrics_collector.increment('assessment.progress.success') self.metrics_collector.histogram('assessment.progress.duration', time.time() - start_time) return result except Exception as e: # Métriques d'erreur self.metrics_collector.increment('assessment.progress.error') self.metrics_collector.increment(f'assessment.progress.error.{type(e).__name__}') raise def _init_metrics(self): """Initialisation du collecteur de métriques.""" # Exemple avec StatsD ou Prometheus return Mock() # Placeholder # =================== RÉSUMÉ DES BÉNÉFICES =================== """ 🎯 BÉNÉFICES DE LA REFACTORISATION : 1. **Respect des principes SOLID** : - Single Responsibility : Chaque service a UNE responsabilité - Open/Closed : Extensible via Strategy pattern (nouveaux types notation) - Liskov Substitution : Interfaces respectées - Interface Segregation : Interfaces spécialisées (ConfigProvider, DatabaseProvider) - Dependency Inversion : Injection de dépendances, plus d'imports circulaires 2. **Performance améliorée** : - Plus de requêtes N+1 (requêtes optimisées dans les providers) - Possibilité de cache au niveau des services - Calculs optimisés 3. **Testabilité** : - Services mockables indépendamment - Tests unitaires isolés - Tests d'intégration facilités 4. **Maintenabilité** : - Code plus lisible et organisé - Responsabilités clairement séparées - Evolution facilitée 5. **Extensibilité** : - Nouveaux types de notation via Strategy pattern - Nouveaux providers pour différents backends - Monitoring et logging ajoutables facilement 6. **Sécurité** : - Plus d'imports circulaires (réduction surface d'attaque) - Validation centralisée dans les services - Meilleur contrôle des dépendances """