Beacon/NOTES.md
Funky Waddle f3c5d3d4fa
Some checks failed
CI / build (push) Has been cancelled
Updating NOTES.md with feature mind map
2026-02-25 00:34:06 -06:00

4.6 KiB

Beacon is an Event Management system built originally for use in the Phred Framework, but opened to the public to install in any codebase. It follows a strict PSR-14 (Event Dispatcher) implementation with an "Enterprise-First" architecture.

Features & Functionality (Mind Map)

  • Event Dispatcher (The "Engine")

    • Primary entry point: $dispatcher->dispatch($event)
    • Returns the modified/original event object (PSR-14)
    • Contract Isolation: Split into BeaconContracts (Interfaces) and Beacon (Implementation).
    • Bridge Support: BeaconBridges for third-party adapters (Symfony, Laravel).
    • Middleware Pipeline: Onion-style pipeline for pre/post dispatch logic.
    • Resiliency: Configurable error handling (Fail-fast vs. Continue/Log).
  • Event Listeners & Providers

    • Listener Provider: Separate PSR-14 component to resolve listeners for an event.
    • Class-based Subscribers: Implement SubscriberInterface for multiple events.
    • Priority System: Higher integer = higher priority (runs earlier).
    • Wildcard Support: Pattern matching (e.g., App.User.*) translated internally to cached regex for efficiency.
    • Lazy Loading: Listeners instantiated only when needed via PSR-11 container.
  • Event Objects & Context

    • Plain Old PHP Objects (POPOs): Any object can be an event.
    • Event Envelope: Optional wrapper to carry immutable metadata/context via a ContextMap.
    • Stoppable Events: Implements StoppableEventInterface (PSR-14).
    • Inheritance Resolution: Automatically resolves listeners for parent classes and interfaces; includes a configuration toggle for high-performance interface skipping.
  • Discovery & Registration

    • Attribute Discovery: PHP 8.2 #[AsEventListener] for zero-config registration.
    • Discovery Logic: A ListenerDiscovery service scans directories (using PSR-4 mapping or explicit paths).
    • Discovery Cache: PSR-16 cache support for scanning and mapping attributes (eliminates runtime scanning).
    • DI Integration: Accept a PSR-11 container to resolve listener instances; discovery yields service IDs or class names for lazy instantiation.
    • Manual Registration: Fluent API for one-off listeners or closures (uses addListener).
  • Integration & Advanced Features

    • Async Dispatching: Offloaded via QueueingDispatcher decorator (in Bridges); serializes the EventEnvelope.
    • PII & Logging: Masking and sensitive data handling live strictly in Bridges or Middleware, keeping the engine core pure.
    • Resiliency & Error Handling: continue mode emits a DispatchFailed event containing the listener, exception, and original event payload for audit/retry.
    • Enterprise Telemetry: Trace execution timing and listener flow.
    • Testing Helpers: PHPUnit traits for easy event assertions.

Concepts: Unified Event Listening

The Dispatcher API

The primary way to trigger an event is through the $dispatch() method.

$dispatcher->dispatch($event);

Listener Registration

We prefer structured class-based listeners, but support closures for lightweight tasks.

  • addListener(string $event, callable $listener, int $priority = 0)
  • addSubscriber(SubscriberInterface $subscriber)

API Examples (Drafts)

Basic Dispatching

$dispatcher = new EventDispatcher($provider);
$event = new UserRegistered($user);

// "Enterprise" Dispatching
$dispatcher->dispatch($event);

Middleware Example

class LoggingMiddleware implements DispatchMiddlewareInterface {
    public function handle(object $event, callable $next): object {
        $log->info("Dispatching: " . $event::class);
        return $next($event);
    }
}

Class-based Subscriber (Unified)

class UserSubscriber implements SubscriberInterface {
    public static function getSubscribedEvents(): array {
        return [
            UserRegistered::class => 'onUserRegistered',
            UserDeleted::class => ['onUserDeleted', 10], // with priority
        ];
    }

    public function onUserRegistered(UserRegistered $event): void { /* ... */ }
}

Attribute-based Discovery

#[AsEventListener(event: UserRegistered::class, priority: 10)]
class SendWelcomeEmailListener {
    public function __invoke(UserRegistered $event): void { /* ... */ }
}

Stoppable Events (with Convenience Trait)

class OrderEvent implements StoppableEventInterface {
    use CanStopPropagation; // Provides stopPropagation() and isPropagationStopped()
}

Contextual Data (Envelope)

$envelope = new EventEnvelope($event, ['request_id' => 'abc-123']);
$dispatcher->dispatch($envelope);