forked from fa/breadcrumb-the-shire
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:
49
lib/App/Module/ModuleEventDispatcher.php
Normal file
49
lib/App/Module/ModuleEventDispatcher.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\App\Module;
|
||||
|
||||
use MintyPHP\App\AppContainer;
|
||||
use MintyPHP\App\Module\Contracts\EventListener;
|
||||
|
||||
/**
|
||||
* Fire-and-forget event dispatcher for module lifecycle events.
|
||||
*
|
||||
* Listeners are declared in module manifests and lazy-resolved from the container.
|
||||
* A failing listener does not break the dispatch chain — the exception is swallowed
|
||||
* so that one misbehaving module cannot disrupt core flows.
|
||||
*/
|
||||
final class ModuleEventDispatcher
|
||||
{
|
||||
/**
|
||||
* @param array<string, list<array{class: class-string, method: string}>> $listenerMap
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $listenerMap,
|
||||
private readonly AppContainer $container
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
public function dispatch(string $event, array $payload): void
|
||||
{
|
||||
$listeners = $this->listenerMap[$event] ?? [];
|
||||
|
||||
foreach ($listeners as $descriptor) {
|
||||
try {
|
||||
$class = $descriptor['class'];
|
||||
$method = $descriptor['method'];
|
||||
|
||||
$listener = $this->container->get($class);
|
||||
if (!$listener instanceof EventListener) {
|
||||
$listener = new $class();
|
||||
}
|
||||
|
||||
$listener->{$method}($event, $payload);
|
||||
} catch (\Throwable) {
|
||||
// Swallow — one failing listener must not break the chain.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user