Files
Classeo/backend/config/services.yaml
Mathias STRASSER 86d00ce733 feat: Afficher les statistiques de notes par matière côté administration
L'admin doit pouvoir voir en un coup d'œil quelles matières sont
actives (notes saisies) pour décider lesquelles peuvent être supprimées
sans perte de données. Auparavant, la suppression d'une matière était
silencieuse : elle cascade-deletait évaluations et notes sans avertir.

La liste des matières affiche désormais les compteurs d'enseignants,
classes, évaluations et notes. La suppression déclenche une confirmation
explicite quand la matière contient des notes, avec récapitulatif des
volumes impactés, pour rendre l'action irréversible consciente.

Côté tests, un endpoint de seeding HTTP remplace les appels docker exec
dans les E2E (gain ~30-60s → 5-10s par test), et un trait partagé
factorise le SQL de seeding entre les deux suites fonctionnelles.
2026-04-21 15:37:25 +02:00

597 lines
27 KiB
YAML

# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
tenant.base_domain: '%env(TENANT_BASE_DOMAIN)%'
app.url: '%env(APP_URL)%'
services:
# default configuration for services in this file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
# Bind activation tokens cache pool (7-day TTL)
Psr\Cache\CacheItemPoolInterface $activationTokensCache: '@activation_tokens.cache'
# Bind users cache pool (no TTL - persistent data)
Psr\Cache\CacheItemPoolInterface $usersCache: '@users.cache'
# Bind refresh tokens cache pool (7-day TTL)
Psr\Cache\CacheItemPoolInterface $refreshTokensCache: '@refresh_tokens.cache'
# Bind password reset tokens cache pool (1-hour TTL)
Psr\Cache\CacheItemPoolInterface $passwordResetTokensCache: '@password_reset_tokens.cache'
# Bind sessions cache pool (7-day TTL)
Psr\Cache\CacheItemPoolInterface $sessionsCache: '@sessions.cache'
# Bind student guardians cache pool (no TTL - persistent data)
Psr\Cache\CacheItemPoolInterface $studentGuardiansCache: '@student_guardians.cache'
# Bind paginated queries cache pool (1h TTL, tag-aware)
Symfony\Contracts\Cache\TagAwareCacheInterface $paginatedQueriesCache: '@paginated_queries.cache'
# Bind named message buses
Symfony\Component\Messenger\MessageBusInterface $eventBus: '@event.bus'
Symfony\Component\Messenger\MessageBusInterface $commandBus: '@command.bus'
Symfony\Component\Messenger\MessageBusInterface $queryBus: '@query.bus'
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# Exclude Domain layers - they should be pure PHP with no framework deps
- '../src/*/Domain/'
# Domain services need to be registered explicitly to avoid framework coupling
# Example: App\Administration\Application\Command\:
# resource: '../src/Administration/Application/Command/'
# Tenant services
App\Shared\Infrastructure\Tenant\TenantResolver:
arguments:
$baseDomain: '%tenant.base_domain%'
App\Shared\Infrastructure\Persistence\Doctrine\TenantAwareConnection:
alias: doctrine.dbal.default_connection
App\Shared\Infrastructure\Tenant\TenantDatabaseSwitcher:
alias: doctrine.dbal.default_connection
# TenantRegistry est configuré par environnement :
# - dev: config/packages/dev/tenant.yaml (tenants de test)
# - prod: à configurer via admin ou env vars
App\Shared\Infrastructure\Tenant\Command\CreateTenantDatabaseCommand:
arguments:
$masterDatabaseUrl: '%env(DATABASE_URL)%'
App\Shared\Infrastructure\Tenant\Command\TenantMigrateCommand:
arguments:
$projectDir: '%kernel.project_dir%'
# Administration services
# Bind Repository interfaces to their implementations
App\Administration\Domain\Repository\ActivationTokenRepository:
alias: App\Administration\Infrastructure\Persistence\Redis\RedisActivationTokenRepository
App\Administration\Infrastructure\Persistence\Cache\CachedUserRepository:
arguments:
$inner: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineUserRepository'
App\Administration\Domain\Repository\UserRepository:
alias: App\Administration\Infrastructure\Persistence\Cache\CachedUserRepository
App\Administration\Infrastructure\Console\MigrateUsersToPostgresCommand:
arguments:
$source: '@App\Administration\Infrastructure\Persistence\Cache\CacheUserRepository'
$target: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineUserRepository'
App\Administration\Application\Port\PasswordHasher:
alias: App\Administration\Infrastructure\Security\SymfonyPasswordHasher
# Clock interface binding
App\Shared\Domain\Clock:
alias: App\Shared\Infrastructure\Clock\SystemClock
# Domain policies (need explicit registration as Domain is excluded from autowiring)
App\Administration\Domain\Policy\ConsentementParentalPolicy:
autowire: true
App\Scolarite\Domain\Policy\VisibiliteNotesPolicy:
autowire: true
# Ports
App\Scolarite\Application\Port\ParentGradeDelayReader:
alias: App\Scolarite\Infrastructure\Service\DatabaseParentGradeDelayReader
# Email handlers
App\Administration\Infrastructure\Messaging\SendActivationConfirmationHandler:
arguments:
$appUrl: '%app.url%'
App\Administration\Infrastructure\Messaging\SendPasswordResetEmailHandler:
arguments:
$appUrl: '%app.url%'
App\Administration\Infrastructure\Messaging\SendPasswordResetConfirmationHandler:
arguments:
$appUrl: '%app.url%'
App\Shared\Infrastructure\Tenant\TenantUrlBuilder:
arguments:
$appUrl: '%app.url%'
$baseDomain: '%tenant.base_domain%'
# Audit Logger Service (writes to append-only audit_log table)
App\Shared\Application\Port\AuditLogger:
alias: App\Shared\Infrastructure\Audit\AuditLogger
App\Shared\Infrastructure\Audit\AuditLogger:
arguments:
$connection: '@doctrine.dbal.master_connection'
$appSecret: '%env(APP_SECRET)%'
App\Shared\Infrastructure\Audit\AuditLogRepository:
arguments:
$connection: '@doctrine.dbal.master_connection'
App\Shared\Infrastructure\Console\ArchiveAuditLogsCommand:
arguments:
$connection: '@doctrine.dbal.master_connection'
App\Shared\Infrastructure\Console\ReviewFailedMessagesCommand:
arguments:
$connection: '@doctrine.dbal.master_connection'
# Audit log handlers (use AuditLogger to write to database)
App\Shared\Infrastructure\Audit\Handler\AuditAuthenticationHandler:
arguments:
$appSecret: '%env(APP_SECRET)%'
# JWT Authentication
App\Administration\Infrastructure\Security\JwtPayloadEnricher:
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }
App\Administration\Infrastructure\Security\DatabaseUserProvider:
arguments:
$userRepository: '@App\Administration\Domain\Repository\UserRepository'
App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineSuperAdminRepository:
arguments:
$connection: '@doctrine.dbal.master_connection'
App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineEstablishmentRepository:
arguments:
$connection: '@doctrine.dbal.master_connection'
App\SuperAdmin\Application\Query\GetEstablishmentsMetrics\GetEstablishmentsMetricsHandler:
arguments:
$connection: '@doctrine.dbal.master_connection'
# Refresh Token Repository
App\Administration\Domain\Repository\RefreshTokenRepository:
alias: App\Administration\Infrastructure\Persistence\Redis\RedisRefreshTokenRepository
# Password Reset Token Repository
App\Administration\Domain\Repository\PasswordResetTokenRepository:
alias: App\Administration\Infrastructure\Persistence\Redis\RedisPasswordResetTokenRepository
# Session Repository
App\Administration\Domain\Repository\SessionRepository:
alias: App\Administration\Infrastructure\Persistence\Redis\RedisSessionRepository
# Class Repository (Story 2.1 - Gestion des classes)
App\Administration\Domain\Repository\ClassRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineClassRepository
# Subject Repository (Story 2.2 - Gestion des matières)
App\Administration\Domain\Repository\SubjectRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSubjectRepository
# Period Configuration Repository (Story 2.3 - Gestion des périodes)
App\Administration\Domain\Repository\PeriodConfigurationRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrinePeriodConfigurationRepository
# Grading Configuration Repository (Story 2.4 - Mode de notation)
App\Administration\Domain\Repository\GradingConfigurationRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineGradingConfigurationRepository
# Class Assignment (Story 3.0 - Affectation élèves aux classes)
App\Administration\Domain\Repository\ClassAssignmentRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineClassAssignmentRepository
# Teacher Assignment (Story 2.8 - Affectation enseignants)
App\Administration\Domain\Repository\TeacherAssignmentRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineTeacherAssignmentRepository
App\Administration\Application\Port\TeacherAssignmentChecker:
alias: App\Administration\Infrastructure\Service\RepositoryTeacherAssignmentChecker
# Teacher Replacement Repository (Story 2.9 - Remplaçants temporaires)
App\Scolarite\Domain\Repository\TeacherReplacementRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineTeacherReplacementRepository
# Homework (Story 5.1 - Création de devoirs)
App\Scolarite\Domain\Repository\HomeworkRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRepository
App\Scolarite\Domain\Repository\HomeworkRuleExceptionRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRuleExceptionRepository
App\Scolarite\Domain\Repository\HomeworkAttachmentRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkAttachmentRepository
# Homework Submissions (Story 5.10 - Rendu de devoir par l'élève)
App\Scolarite\Domain\Repository\HomeworkSubmissionRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkSubmissionRepository
App\Scolarite\Domain\Repository\SubmissionAttachmentRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineSubmissionAttachmentRepository
App\Scolarite\Application\Port\ClassStudentsReader:
alias: App\Scolarite\Infrastructure\Service\DoctrineClassStudentsReader
App\Scolarite\Domain\Service\DueDateValidator:
autowire: true
App\Scolarite\Domain\Service\HomeworkDuplicator:
autowire: true
App\Scolarite\Application\Port\HtmlSanitizer:
alias: App\Scolarite\Infrastructure\Service\HomeworkHtmlSanitizer
App\Scolarite\Infrastructure\Service\HomeworkHtmlSanitizer:
arguments:
$homeworkSanitizer: '@html_sanitizer.sanitizer.homework_sanitizer'
App\Scolarite\Application\Port\FileStorage:
alias: App\Scolarite\Infrastructure\Storage\S3FileStorage
App\Scolarite\Infrastructure\Storage\LocalFileStorage:
arguments:
$storagePath: '%kernel.project_dir%/var/storage'
App\Scolarite\Infrastructure\Storage\S3FileStorage:
arguments:
$endpoint: '%env(S3_ENDPOINT)%'
$bucket: '%env(S3_BUCKET)%'
$key: '%env(S3_KEY)%'
$secret: '%env(S3_SECRET)%'
$region: '%env(S3_REGION)%'
# Schedule (Story 4.1 - Emploi du temps)
App\Scolarite\Domain\Repository\ScheduleSlotRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineScheduleSlotRepository
App\Scolarite\Domain\Repository\ScheduleExceptionRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineScheduleExceptionRepository
App\Scolarite\Domain\Service\ScheduleConflictDetector:
autowire: true
App\Scolarite\Application\Port\EnseignantAffectationChecker:
alias: App\Scolarite\Infrastructure\Service\CurrentYearEnseignantAffectationChecker
App\Scolarite\Domain\Repository\EvaluationRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationRepository
App\Scolarite\Domain\Repository\GradeRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineGradeRepository
App\Scolarite\Domain\Repository\AppreciationTemplateRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineAppreciationTemplateRepository
App\Scolarite\Application\Port\EvaluationGradesChecker:
alias: App\Scolarite\Infrastructure\Service\NoGradesEvaluationGradesChecker
# Competency repositories (Story 6.5 - Mode compétences)
App\Scolarite\Domain\Repository\CompetencyFrameworkRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyFrameworkRepository
App\Scolarite\Domain\Repository\CompetencyRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyRepository
App\Scolarite\Domain\Repository\CompetencyEvaluationRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyEvaluationRepository
App\Scolarite\Domain\Repository\StudentCompetencyResultRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentCompetencyResultRepository
App\Scolarite\Domain\Repository\CustomCompetencyLevelRepository:
alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCustomCompetencyLevelRepository
# Averages (Story 6.3 - Calcul automatique des moyennes)
App\Scolarite\Domain\Service\AverageCalculator:
autowire: true
App\Scolarite\Domain\Service\TeacherStatisticsCalculator:
autowire: true
App\Scolarite\Application\Service\RecalculerMoyennesService:
autowire: true
App\Scolarite\Application\Port\PeriodFinder:
alias: App\Scolarite\Infrastructure\Service\DoctrinePeriodFinder
App\Scolarite\Application\Port\TeacherStatisticsReader:
alias: App\Scolarite\Infrastructure\ReadModel\DbalTeacherStatisticsReader
App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationStatisticsRepository:
autowire: true
App\Scolarite\Infrastructure\Cache\CachingEvaluationStatisticsRepository:
arguments:
$inner: '@App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationStatisticsRepository'
$cache: '@student_averages.cache'
App\Scolarite\Domain\Repository\EvaluationStatisticsRepository:
alias: App\Scolarite\Infrastructure\Cache\CachingEvaluationStatisticsRepository
App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentAverageRepository:
autowire: true
App\Scolarite\Infrastructure\Cache\CachingStudentAverageRepository:
arguments:
$inner: '@App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentAverageRepository'
$cache: '@student_averages.cache'
App\Scolarite\Domain\Repository\StudentAverageRepository:
alias: App\Scolarite\Infrastructure\Cache\CachingStudentAverageRepository
# Super Admin Repositories (Story 2.10 - Multi-établissements)
App\SuperAdmin\Domain\Repository\SuperAdminRepository:
alias: App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineSuperAdminRepository
App\SuperAdmin\Domain\Repository\EstablishmentRepository:
alias: App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineEstablishmentRepository
# Provisioning (Story 2.17 - Provisioning automatique)
App\SuperAdmin\Infrastructure\Provisioning\TenantDatabaseCreator:
arguments:
$connection: '@doctrine.dbal.master_connection'
App\SuperAdmin\Infrastructure\Provisioning\TenantMigrator:
arguments:
$projectDir: '%kernel.project_dir%'
$masterDatabaseUrl: '%env(DATABASE_URL)%'
App\SuperAdmin\Application\Port\TenantProvisioner:
alias: App\SuperAdmin\Infrastructure\Provisioning\DatabaseTenantProvisioner
App\SuperAdmin\Infrastructure\Provisioning\ProvisionEstablishmentHandler:
arguments:
$masterDatabaseUrl: '%env(DATABASE_URL)%'
# School Calendar Repository (Story 2.11 - Calendrier scolaire)
App\Administration\Domain\Model\SchoolCalendar\SchoolCalendarRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSchoolCalendarRepository
App\Administration\Application\Port\OfficialCalendarProvider:
alias: App\Administration\Infrastructure\Service\JsonOfficialCalendarProvider
App\Administration\Infrastructure\Service\JsonOfficialCalendarProvider:
arguments:
$dataDirectory: '%kernel.project_dir%/var/data/calendar'
# School Branding (Story 2.13 - Personnalisation visuelle)
App\Administration\Domain\Model\SchoolBranding\ContrastValidator:
autowire: true
App\Administration\Domain\Repository\SchoolBrandingRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSchoolBrandingRepository
# Homework Rules Repository
App\Administration\Domain\Repository\HomeworkRulesRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRulesRepository
App\Administration\Domain\Repository\HomeworkRulesHistoryRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRulesHistoryRepository
App\Administration\Application\Port\LogoStorage:
alias: App\Administration\Infrastructure\Storage\LocalLogoStorage
App\Administration\Infrastructure\Storage\LocalLogoStorage:
arguments:
$uploadDir: '%kernel.project_dir%/public/uploads'
$publicPath: '/uploads'
App\Administration\Application\Port\ImageProcessor:
alias: App\Administration\Infrastructure\Storage\ImagickImageProcessor
# Import Batch Repository (Story 3.1 - Import élèves via CSV)
App\Administration\Domain\Repository\ImportBatchRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineImportBatchRepository
# Saved Column Mapping Repository (Story 3.1 - T3.3 Réutilisation des mappings)
App\Administration\Domain\Repository\SavedColumnMappingRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSavedColumnMappingRepository
# Teacher Import Batch Repository (Story 3.2 - Import enseignants via CSV)
App\Administration\Domain\Repository\TeacherImportBatchRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineTeacherImportBatchRepository
# Saved Teacher Column Mapping Repository (Story 3.2 - Réutilisation des mappings enseignants)
App\Administration\Domain\Repository\SavedTeacherColumnMappingRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSavedTeacherColumnMappingRepository
# Parent Invitation Repository (Story 3.3 - Invitation parents)
App\Administration\Domain\Repository\ParentInvitationRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineParentInvitationRepository
# Student Guardian Repository (Story 2.7 - Liaison parents-enfants)
App\Administration\Infrastructure\Persistence\Cache\CacheStudentGuardianRepository:
arguments:
$inner: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineStudentGuardianRepository'
App\Administration\Domain\Repository\StudentGuardianRepository:
alias: App\Administration\Infrastructure\Persistence\Cache\CacheStudentGuardianRepository
# Paginated Read Model Ports
App\Administration\Application\Port\PaginatedUsersReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedUsersReader
App\Administration\Application\Port\PaginatedClassesReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedClassesReader
App\Administration\Application\Port\PaginatedSubjectsReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedSubjectsReader
App\Administration\Application\Port\PaginatedAssignmentsReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedAssignmentsReader
App\Administration\Application\Port\PaginatedParentInvitationsReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedParentInvitationsReader
App\Administration\Application\Port\PaginatedStudentImageRightsReader:
alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedStudentImageRightsReader
# GradeExistenceChecker (stub until Notes module exists)
App\Administration\Application\Port\GradeExistenceChecker:
alias: App\Administration\Infrastructure\Service\NoOpGradeExistenceChecker
# SubjectGradeStatsReader (implémentation Scolarite via SQL)
App\Administration\Application\Port\SubjectGradeStatsReader:
alias: App\Scolarite\Infrastructure\Service\DoctrineSubjectGradeStatsReader
# ActiveRoleStore (session-scoped cache for active role switching)
App\Administration\Application\Port\ActiveRoleStore:
alias: App\Administration\Infrastructure\Service\CacheActiveRoleStore
# GeoLocation Service (null implementation - no geolocation)
App\Administration\Application\Port\GeoLocationService:
alias: App\Administration\Infrastructure\Service\NullGeoLocationService
# Password Reset Processor with rate limiters
App\Administration\Infrastructure\Api\Processor\RequestPasswordResetProcessor:
arguments:
$passwordResetByEmailLimiter: '@limiter.password_reset_by_email'
$passwordResetByIpLimiter: '@limiter.password_reset_by_ip'
# Parent Activation Processor with rate limiter
App\Administration\Infrastructure\Api\Processor\ActivateParentInvitationProcessor:
arguments:
$parentActivationByIpLimiter: '@limiter.parent_activation_by_ip'
# Login handlers
App\Administration\Infrastructure\Security\LoginSuccessHandler:
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccess }
App\Administration\Infrastructure\Security\LoginFailureHandler:
tags:
- { name: security.authentication_failure_handler, firewall: api_login }
# Rate Limiter (délai Fibonacci + CAPTCHA + blocage IP)
App\Shared\Infrastructure\RateLimit\LoginRateLimiter:
arguments:
$cache: '@cache.rate_limiter'
App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
alias: App\Shared\Infrastructure\RateLimit\LoginRateLimiter
# Rate Limit Listener (vérifie le rate limit AVANT authentification)
App\Shared\Infrastructure\RateLimit\LoginRateLimitListener:
arguments:
$rateLimiterCache: '@cache.rate_limiter'
# Turnstile CAPTCHA Validator
# failOpen: true en dev (ne pas bloquer si API down), false en prod (sécurité)
App\Shared\Infrastructure\Captcha\TurnstileValidator:
arguments:
$secretKey: '%env(TURNSTILE_SECRET_KEY)%'
$failOpen: '%env(bool:default::TURNSTILE_FAIL_OPEN)%'
App\Shared\Infrastructure\Captcha\TurnstileValidatorInterface:
alias: App\Shared\Infrastructure\Captcha\TurnstileValidator
# =============================================================================
# Monitoring & Observability (Story 1.8)
# =============================================================================
# Prometheus CollectorRegistry - uses Redis for persistence between requests
Prometheus\Storage\Redis:
factory: ['App\Shared\Infrastructure\Monitoring\PrometheusStorageFactory', 'createRedisStorage']
arguments:
$redisUrl: '%env(REDIS_URL)%'
Prometheus\CollectorRegistry:
arguments:
$storageAdapter: '@Prometheus\Storage\Redis'
# Sentry/GlitchTip PII scrubber callback
App\Shared\Infrastructure\Monitoring\SentryBeforeSendCallback: ~
# Infrastructure Health Checker - shared service for health checks (DRY)
App\Shared\Infrastructure\Monitoring\InfrastructureHealthChecker:
arguments:
$connection: '@doctrine.dbal.master_connection'
$redisUrl: '%env(REDIS_URL)%'
# Interface alias for InfrastructureHealthChecker (allows testing with stubs)
App\Shared\Infrastructure\Monitoring\InfrastructureHealthCheckerInterface:
alias: App\Shared\Infrastructure\Monitoring\InfrastructureHealthChecker
# Health Check Controller - uses shared InfrastructureHealthChecker
App\Shared\Infrastructure\Monitoring\HealthCheckController: ~
# Metrics Controller - restricted to internal networks in production
App\Shared\Infrastructure\Monitoring\MetricsController:
arguments:
$appEnv: '%kernel.environment%'
# Health Metrics Collector - exposes health_check_status gauge
App\Shared\Infrastructure\Monitoring\HealthMetricsCollector: ~
# Interface alias for HealthMetricsCollector (allows testing with stubs)
App\Shared\Infrastructure\Monitoring\HealthMetricsCollectorInterface:
alias: App\Shared\Infrastructure\Monitoring\HealthMetricsCollector
# Sentry context enricher - adds tenant/user/correlation_id to error reports
# Explicitly registered to ensure HubInterface dependency is resolved
App\Shared\Infrastructure\Monitoring\SentryContextEnricher:
arguments:
$sentryHub: '@Sentry\State\HubInterface'
# Monolog processors for structured logging
App\Shared\Infrastructure\Monitoring\CorrelationIdLogProcessor:
tags:
- { name: monolog.processor }
App\Shared\Infrastructure\Monitoring\PiiScrubberLogProcessor:
tags:
- { name: monolog.processor }
# =============================================================================
# Messenger & Async (Story 2.5b)
# =============================================================================
# Fibonacci retry strategy for async transport
App\Shared\Infrastructure\Messenger\FibonacciRetryStrategy: ~
# Dead-letter alert: sends admin email when message exhausts all retries
App\Shared\Infrastructure\Messenger\DeadLetterAlertHandler:
arguments:
$adminEmail: '%env(ADMIN_ALERT_EMAIL)%'
tags:
- { name: kernel.event_listener, event: Symfony\Component\Messenger\Event\WorkerMessageFailedEvent }
# Messenger metrics middleware (handled/failed counters)
App\Shared\Infrastructure\Messenger\MessengerMetricsMiddleware: ~
# Messenger queue metrics collector (messages waiting gauge)
App\Shared\Infrastructure\Monitoring\MessengerMetricsCollector: ~
# =============================================================================
# Test environment overrides
# =============================================================================
when@test:
services:
# Use null rate limiter in test environment to avoid IP blocking during E2E tests
App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
alias: App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter
App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter:
autowire: true