Files
Classeo/backend/config/services.yaml
Mathias STRASSER 713e408773
Some checks failed
CI / Naming Conventions (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-10 15:24:27 +02:00

587 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\Application\Service\RecalculerMoyennesService:
autowire: true
App\Scolarite\Application\Port\PeriodFinder:
alias: App\Scolarite\Infrastructure\Service\DoctrinePeriodFinder
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
# 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