refactor(audit): extract audit domain into self-contained module
Move the entire audit subsystem (system audit, API audit, import audit, user lifecycle audit, frontend telemetry) from core into modules/audit/. Core decoupling via interface-based injection: - AuditRecorderInterface replaces SystemAuditService in 10+ core services - UserLifecycleAuditInterface / ImportAuditInterface for specialized flows - NullAuditRecorder fallback when audit module is disabled - ApiBootstrap/ApiResponse use null-safe callable resolvers Module structure (modules/audit/): - Manifest with routes, permissions, scheduler jobs, authorization policy - 9 services, 8 repositories, 6 domain enums, 4 job handlers - 33 page files, 4 JS files, 8 test files, migration scripts, i18n Core cleanup: - OperationsAuthorizationPolicy, UiCapabilityMap, PermissionService surgically cleaned of audit-specific constants - Sidebar template cleared of hardcoded audit navigation - AuditModuleIsolationContractTest ensures no future core→module coupling All quality gates pass: 1346 tests (19,276 assertions), PHPStan level 5 clean, architecture contracts verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ use MintyPHP\Service\Access\AuthorizationService;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
use MintyPHP\Service\Access\RoleService;
|
||||
use MintyPHP\Service\Access\UiAccessService;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\Directory\DirectoryServicesFactory;
|
||||
|
||||
final class AccessRegistrar implements ContainerRegistrar
|
||||
@@ -40,7 +40,7 @@ final class AccessRegistrar implements ContainerRegistrar
|
||||
$c->get(AccessServicesFactory::class)->createUserRoleRepository(),
|
||||
$c->get(PermissionService::class),
|
||||
$c->get(RoleRepository::class),
|
||||
$c->get(SystemAuditService::class)
|
||||
$c->get(AuditRecorderInterface::class)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace MintyPHP\App\Container\Registrars;
|
||||
use MintyPHP\App\AppContainer;
|
||||
use MintyPHP\App\Container\ContainerRegistrar;
|
||||
use MintyPHP\App\Module\ModuleRegistry;
|
||||
use MintyPHP\Http\ApiSystemAuditReporter;
|
||||
use MintyPHP\Http\CookieStore;
|
||||
use MintyPHP\Http\CookieStoreInterface;
|
||||
use MintyPHP\Http\Input\RequestInputFactory;
|
||||
@@ -18,15 +17,7 @@ use MintyPHP\Repository\Search\SearchQueryRepository;
|
||||
use MintyPHP\Repository\Stats\AdminStatsRepository;
|
||||
use MintyPHP\Repository\System\SystemHealthRepository;
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepository;
|
||||
use MintyPHP\Repository\User\UserReadRepository;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
use MintyPHP\Service\Audit\ApiAuditService;
|
||||
use MintyPHP\Service\Audit\AuditMetadataEnricher;
|
||||
use MintyPHP\Service\Audit\AuditServicesFactory;
|
||||
use MintyPHP\Service\Audit\FrontendTelemetryIngestService;
|
||||
use MintyPHP\Service\Audit\ImportAuditService;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\UserLifecycleAuditService;
|
||||
use MintyPHP\Service\CustomField\CustomFieldServicesFactory;
|
||||
use MintyPHP\Service\CustomField\TenantCustomFieldService;
|
||||
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
|
||||
@@ -42,7 +33,6 @@ use MintyPHP\Service\Scheduler\SchedulerServicesFactory;
|
||||
use MintyPHP\Service\Search\SearchDataService;
|
||||
use MintyPHP\Service\Security\RateLimiterService;
|
||||
use MintyPHP\Service\Security\SecurityServicesFactory;
|
||||
use MintyPHP\Service\Settings\SettingsFrontendTelemetryGateway;
|
||||
use MintyPHP\Service\Stats\AdminStatsViewDataService;
|
||||
use MintyPHP\Service\System\SystemHealthService;
|
||||
use MintyPHP\Service\System\SystemInfoService;
|
||||
@@ -71,27 +61,13 @@ final class AppServicesRegistrar implements ContainerRegistrar
|
||||
$container->set(RateLimiterService::class, static fn (AppContainer $c): RateLimiterService => $c->get(SecurityServicesFactory::class)->createRateLimiterService());
|
||||
$container->set(MailService::class, static fn (AppContainer $c): MailService => $c->get(MailServicesFactory::class)->createMailService());
|
||||
$container->set(MailLogService::class, static fn (AppContainer $c): MailLogService => $c->get(MailServicesFactory::class)->createMailLogService());
|
||||
$container->set(ApiAuditService::class, static fn (AppContainer $c): ApiAuditService => $c->get(AuditServicesFactory::class)->createApiAuditService());
|
||||
$container->set(UserLifecycleAuditService::class, static fn (AppContainer $c): UserLifecycleAuditService => $c->get(AuditServicesFactory::class)->createUserLifecycleAuditService());
|
||||
$container->set(SystemAuditService::class, static fn (AppContainer $c): SystemAuditService => $c->get(AuditServicesFactory::class)->createSystemAuditService());
|
||||
$container->set(FrontendTelemetryIngestService::class, static fn (AppContainer $c): FrontendTelemetryIngestService => new FrontendTelemetryIngestService(
|
||||
$c->get(SystemAuditService::class),
|
||||
$c->get(SettingsFrontendTelemetryGateway::class),
|
||||
$c->get(RateLimiterService::class),
|
||||
$c->get(SessionStoreInterface::class)
|
||||
));
|
||||
$container->set(ApiSystemAuditReporter::class, static fn (AppContainer $c): ApiSystemAuditReporter => new ApiSystemAuditReporter(
|
||||
$c->get(SystemAuditService::class)
|
||||
));
|
||||
// Audit services are registered by the audit module's AuditContainerRegistrar.
|
||||
$container->set(ImportService::class, static fn (AppContainer $c): ImportService => $c->get(ImportServicesFactory::class)->createImportService());
|
||||
$container->set(ImportAuditService::class, static fn (AppContainer $c): ImportAuditService => $c->get(ImportServicesFactory::class)->createImportAuditService());
|
||||
$container->set(ScheduledJobService::class, static fn (AppContainer $c): ScheduledJobService => $c->get(SchedulerServicesFactory::class)->createScheduledJobService());
|
||||
$container->set(SchedulerRunService::class, static fn (AppContainer $c): SchedulerRunService => $c->get(SchedulerServicesFactory::class)->createSchedulerRunService());
|
||||
$container->set(TenantCustomFieldService::class, static fn (AppContainer $c): TenantCustomFieldService => $c->get(CustomFieldServicesFactory::class)->createTenantCustomFieldService());
|
||||
$container->set(UserCustomFieldValueService::class, static fn (AppContainer $c): UserCustomFieldValueService => $c->get(CustomFieldServicesFactory::class)->createUserCustomFieldValueService());
|
||||
$container->set(AuditMetadataEnricher::class, static fn (AppContainer $c): AuditMetadataEnricher => new AuditMetadataEnricher(
|
||||
$c->get(UserReadRepository::class)
|
||||
));
|
||||
// AuditMetadataEnricher is registered by the audit module's AuditContainerRegistrar.
|
||||
$container->set(GridUserCountEnricher::class, static fn (): GridUserCountEnricher => new GridUserCountEnricher());
|
||||
$container->set(IntendedUrlService::class, static fn (): IntendedUrlService => new IntendedUrlService());
|
||||
$container->set(RequestInputFactory::class, static fn (): RequestInputFactory => new RequestInputFactory());
|
||||
|
||||
@@ -8,7 +8,6 @@ use MintyPHP\Repository\Search\SearchQueryRepository;
|
||||
use MintyPHP\Repository\Stats\AdminStatsRepository;
|
||||
use MintyPHP\Repository\Support\DatabaseSessionRepository;
|
||||
use MintyPHP\Service\Access\AccessRepositoryFactory;
|
||||
use MintyPHP\Service\Audit\AuditRepositoryFactory;
|
||||
use MintyPHP\Service\Auth\AuthRepositoryFactory;
|
||||
use MintyPHP\Service\Directory\DirectoryRepositoryFactory;
|
||||
use MintyPHP\Service\Import\ImportRepositoryFactory;
|
||||
@@ -24,7 +23,6 @@ final class RepositoryFactoryRegistrar implements ContainerRegistrar
|
||||
public function register(AppContainer $container): void
|
||||
{
|
||||
$container->set(SettingRepositoryFactory::class, static fn (): SettingRepositoryFactory => new SettingRepositoryFactory());
|
||||
$container->set(AuditRepositoryFactory::class, static fn (): AuditRepositoryFactory => new AuditRepositoryFactory());
|
||||
$container->set(SecurityRepositoryFactory::class, static fn (): SecurityRepositoryFactory => new SecurityRepositoryFactory());
|
||||
$container->set(MailRepositoryFactory::class, static fn (): MailRepositoryFactory => new MailRepositoryFactory());
|
||||
$container->set(ImportRepositoryFactory::class, static fn (): ImportRepositoryFactory => new ImportRepositoryFactory());
|
||||
|
||||
@@ -18,8 +18,12 @@ use MintyPHP\Service\Access\AccessRepositoryFactory;
|
||||
use MintyPHP\Service\Access\AccessServicesFactory;
|
||||
use MintyPHP\Service\Access\AssignableRoleService;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
use MintyPHP\Service\Audit\AuditRepositoryFactory;
|
||||
use MintyPHP\Service\Audit\AuditServicesFactory;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\Audit\ImportAuditInterface;
|
||||
use MintyPHP\Service\Audit\NullAuditRecorder;
|
||||
use MintyPHP\Service\Audit\NullImportAudit;
|
||||
use MintyPHP\Service\Audit\NullUserLifecycleAudit;
|
||||
use MintyPHP\Service\Audit\UserLifecycleAuditInterface;
|
||||
use MintyPHP\Service\Auth\AuthGatewayFactory;
|
||||
use MintyPHP\Service\Auth\AuthRepositoryFactory;
|
||||
use MintyPHP\Service\Auth\AuthServicesFactory;
|
||||
@@ -52,12 +56,9 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
|
||||
$container->set(SettingServicesFactory::class, static fn (AppContainer $c): SettingServicesFactory => new SettingServicesFactory(
|
||||
$c->get(SettingRepositoryFactory::class)
|
||||
));
|
||||
$container->set(AuditServicesFactory::class, static fn (AppContainer $c): AuditServicesFactory => new AuditServicesFactory(
|
||||
$c->get(AuditRepositoryFactory::class),
|
||||
$c->get(SettingServicesFactory::class)->createSettingsSystemAuditGateway(),
|
||||
$c->get(RequestRuntimeInterface::class),
|
||||
$c->get(SessionStoreInterface::class)
|
||||
));
|
||||
// Audit interfaces are NOT registered here — they are set AFTER module registrars
|
||||
// in registerContainer.php so the audit module can provide real implementations.
|
||||
// If the audit module is disabled, null implementations are used as fallback.
|
||||
$container->set(SecurityServicesFactory::class, static fn (AppContainer $c): SecurityServicesFactory => new SecurityServicesFactory(
|
||||
$c->get(SecurityRepositoryFactory::class)
|
||||
));
|
||||
@@ -81,20 +82,20 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
|
||||
$c->get(AccessServicesFactory::class),
|
||||
$c->get(SettingServicesFactory::class),
|
||||
$c->get(DirectoryServicesFactory::class),
|
||||
$c->get(ImportRepositoryFactory::class),
|
||||
$c->get(ImportAuditInterface::class),
|
||||
$c->get(TenantScopeService::class),
|
||||
$c->get(SessionStoreInterface::class)
|
||||
));
|
||||
$container->set(SchedulerServicesFactory::class, static fn (AppContainer $c): SchedulerServicesFactory => new SchedulerServicesFactory(
|
||||
$c->get(UserServicesFactory::class),
|
||||
$c->get(AuditServicesFactory::class),
|
||||
$c->get(AuditRecorderInterface::class),
|
||||
$c->get(DatabaseSessionRepository::class),
|
||||
$c->get(SchedulerRepositoryFactory::class),
|
||||
$c
|
||||
));
|
||||
$container->set(DirectoryServicesFactory::class, static fn (AppContainer $c): DirectoryServicesFactory => new DirectoryServicesFactory(
|
||||
$c->get(UserServicesFactory::class),
|
||||
$c->get(AuditServicesFactory::class),
|
||||
$c->get(AuditRecorderInterface::class),
|
||||
$c->get(DirectoryRepositoryFactory::class),
|
||||
$c->get(DirectoryGatewayFactory::class)
|
||||
));
|
||||
@@ -111,7 +112,7 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
|
||||
));
|
||||
$container->set(AccessGatewayFactory::class, static fn (AppContainer $c): AccessGatewayFactory => new AccessGatewayFactory(
|
||||
$c->get(AccessRepositoryFactory::class),
|
||||
$c->get(AuditServicesFactory::class),
|
||||
$c->get(AuditRecorderInterface::class),
|
||||
$c->get(SessionStoreInterface::class)
|
||||
));
|
||||
$container->set(AccessPolicyFactory::class, static fn (AppContainer $c): AccessPolicyFactory => new AccessPolicyFactory(
|
||||
@@ -127,7 +128,8 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
|
||||
$c->get(DirectoryRepositoryFactory::class),
|
||||
));
|
||||
$container->set(UserServicesFactory::class, static fn (AppContainer $c): UserServicesFactory => new UserServicesFactory(
|
||||
$c->get(AuditServicesFactory::class),
|
||||
$c->get(AuditRecorderInterface::class),
|
||||
$c->get(UserLifecycleAuditInterface::class),
|
||||
$c->get(UserRepositoryFactory::class),
|
||||
$c->get(UserGatewayFactory::class),
|
||||
$c->get(DatabaseSessionRepository::class),
|
||||
@@ -144,7 +146,7 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
|
||||
));
|
||||
$container->set(AuthServicesFactory::class, static fn (AppContainer $c): AuthServicesFactory => new AuthServicesFactory(
|
||||
$c->get(UserServicesFactory::class),
|
||||
$c->get(AuditServicesFactory::class),
|
||||
$c->get(AuditRecorderInterface::class),
|
||||
$c->get(MailServicesFactory::class),
|
||||
$c->get(AuthRepositoryFactory::class),
|
||||
$c->get(AuthGatewayFactory::class),
|
||||
|
||||
@@ -7,7 +7,7 @@ use MintyPHP\App\Container\ContainerRegistrar;
|
||||
use MintyPHP\Repository\Auth\ApiTokenRepository;
|
||||
use MintyPHP\Repository\Auth\RememberTokenRepository;
|
||||
use MintyPHP\Service\Access\RoleService;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\Branding\BrandingFaviconService;
|
||||
use MintyPHP\Service\Branding\BrandingLogoService;
|
||||
use MintyPHP\Service\Branding\BrandingServicesFactory;
|
||||
@@ -63,7 +63,7 @@ final class SettingsRegistrar implements ContainerRegistrar
|
||||
$c->get(DepartmentService::class),
|
||||
$c->get(RememberTokenRepository::class),
|
||||
$c->get(ApiTokenRepository::class),
|
||||
$c->get(SystemAuditService::class)
|
||||
$c->get(AuditRecorderInterface::class)
|
||||
));
|
||||
$container->set(ThemeConfigGateway::class, static fn (AppContainer $c): ThemeConfigGateway => $c->get(SettingServicesFactory::class)->createThemeConfigGateway());
|
||||
// Branding services are registered here because they depend on settings gateways
|
||||
|
||||
Reference in New Issue
Block a user