feat: module architecture improvements — session keys, dependency graph, event dispatcher, deactivation hooks

Six targeted improvements to the modular monolith platform:

1. Fix AddressBook session key prefix to follow module.<id>.* convention
2. Move ADDRESS_BOOK_VIEW permission constant from core PermissionService into module
3. Add declarative JSON schema for module manifests (.agents/contracts/)
4. Add `requires` field with missing-dependency and circular-dependency detection
5. Add lightweight fire-and-forget event dispatcher (user.created/deleted/login/logout)
6. Add module deactivation hook interface and CLI script (bin/module-deactivate.php)

Includes 15 new architecture/unit tests covering all new functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:20:13 +01:00
parent 866d43e15a
commit ef72b34c40
31 changed files with 1041 additions and 75 deletions

View File

@@ -2,6 +2,7 @@
namespace MintyPHP\Service\Auth;
use MintyPHP\App\Module\ModuleEventDispatcher;
use MintyPHP\Auth;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
@@ -42,7 +43,8 @@ class AuthService
private readonly TenantSsoService $tenantSsoService,
private readonly AuthSessionTenantContextService $authSessionTenantContextService,
private readonly SystemAuditService $systemAuditService,
private readonly SessionStoreInterface $sessionStore
private readonly SessionStoreInterface $sessionStore,
private readonly ?ModuleEventDispatcher $eventDispatcher = null
) {
}
@@ -130,6 +132,11 @@ class AuthService
'actor_tenant_id' => $this->currentTenantIdFromSession(),
]);
$this->eventDispatcher?->dispatch('user.login', [
'user_id' => $userId,
'provider' => 'local',
]);
return ['ok' => true];
}
@@ -218,6 +225,12 @@ class AuthService
'actor_tenant_id' => $this->currentTenantIdFromSession(),
'metadata' => ['provider' => $loginProvider],
]);
$this->eventDispatcher?->dispatch('user.login', [
'user_id' => $userId,
'provider' => $loginProvider,
]);
return ['ok' => true, 'user' => $user];
}
@@ -276,6 +289,11 @@ class AuthService
{
$actorUserId = (int) ($this->sessionUser()['id'] ?? 0);
$actorTenantId = $this->currentTenantIdFromSession();
$this->eventDispatcher?->dispatch('user.logout', [
'user_id' => $actorUserId,
]);
$this->rememberMeService->forgetCurrentUser();
$this->logoutWithModuleCleanup();
$this->recordAuthEvent('auth.logout', 'success', [