# Event System

This guide covers the event-driven architecture of the framework.

## Overview

The framework provides a sophisticated event system built on two core components:

- **EventBus**: Simple, lightweight interface for basic event dispatching
- **EventDispatcher**: Full-featured implementation with handler management, priorities, and propagation control

**Key Features**:
- Attribute-based event handler registration via `#[OnEvent]`
- Priority-based handler execution
- Event propagation control (stop propagation)
- Support for inheritance and interface-based event matching
- Automatic handler discovery via framework's attribute system
- Domain events for business logic decoupling
- Application lifecycle events for framework integration

## EventBus vs EventDispatcher

### EventBus Interface

**Purpose**: Simple contract for event dispatching

```php
interface EventBus
{
    public function dispatch(object $event): void;
}
```

**When to use**:
- Simple event dispatching without return values
- Fire-and-forget events
- When you don't need handler results
- Dependency injection when you want interface-based type hints

**Example Usage**:
```php
final readonly class OrderService
{
    public function __construct(
        private EventBus $eventBus
    ) {}

    public function createOrder(CreateOrderCommand $command): Order
    {
        $order = Order::create($command);

        // Fire event without caring about results
        $this->eventBus->dispatch(new OrderCreatedEvent($order));

        return $order;
    }
}
```

### EventDispatcher Implementation

**Purpose**: Full-featured event system with handler management and results

**Key Features**:
- Returns array of handler results
- Priority-based handler execution (highest priority first)
- Stop propagation support
- Manual handler registration via `addHandler()` or `listen()`
- Automatic handler discovery via `#[OnEvent]` attribute
- Inheritance and interface matching

**When to use**:
- Need handler return values for processing
- Want priority control over execution order
- Need to stop event propagation conditionally
- Require manual handler registration
- Building complex event workflows

**Example Usage**:
```php
final readonly class PaymentProcessor
{
    public function __construct(
        private EventDispatcher $dispatcher
    ) {}

    public function processPayment(Payment $payment): PaymentResult
    {
        // Dispatch and collect results from all handlers
        $results = $this->dispatcher->dispatch(
            new PaymentProcessingEvent($payment)
        );

        // Process handler results
        foreach ($results as $result) {
            if ($result instanceof PaymentValidationFailure) {
                throw new PaymentException($result->reason);
            }
        }

        return new PaymentResult($payment, $results);
    }
}
```

### Implementation Relationship

```php
// EventDispatcher implements EventBus interface
final class EventDispatcher implements EventBus
{
    public function dispatch(object $event): array
    {
        // Full implementation with handler management
    }
}

// In DI Container
$container->singleton(EventBus::class, EventDispatcher::class);
$container->singleton(EventDispatcher::class, EventDispatcher::class);
```

**Recommendation**: Use `EventDispatcher` for type hints when you need full features, use `EventBus` interface when you want loose coupling and don't need results.

## Domain Events

### What are Domain Events?

Domain Events represent significant state changes or occurrences in your business domain. They enable loose coupling between domain components.

**Characteristics**:
- Immutable (`readonly` classes)
- Named in past tense (OrderCreated, UserRegistered, PaymentCompleted)
- Contain only relevant domain data
- No business logic (pure data objects)
- Include timestamp for audit trail

### Creating Domain Events

```php
namespace App\Domain\Order\Events;

use App\Domain\Order\ValueObjects\OrderId;
use App\Framework\Core\ValueObjects\Timestamp;

final readonly class OrderCreatedEvent
{
    public readonly Timestamp $occurredAt;

    public function __construct(
        public OrderId $orderId,
        public UserId $userId,
        public Money $total,
        ?Timestamp $occurredAt = null
    ) {
        $this->occurredAt = $occurredAt ?? Timestamp::now();
    }
}
```

**Best Practices**:
- Use Value Objects for event properties
- Include timestamp for audit trail
- Keep events focused (single responsibility)
- Version events for backward compatibility
- Document event payload in PHPDoc

### Framework Lifecycle Events

The framework provides built-in events for application lifecycle:

#### ApplicationBooted

**Triggered**: After application bootstrap completes

```php
final readonly class ApplicationBooted
{
    public function __construct(
        public \DateTimeImmutable $bootTime,
        public string $environment,
        public Version $version,
        public \DateTimeImmutable $occurredAt = new \DateTimeImmutable()
    ) {}
}
```

**Use Cases**:
- Initialize services after boot
- Start background workers
- Setup scheduled tasks
- Warm caches

#### BeforeHandleRequest

**Triggered**: Before HTTP request processing

```php
final readonly class BeforeHandleRequest
{
    public function __construct(
        public Request $request,
        public Timestamp $timestamp,
        public array $context = []
    ) {}
}
```

**Use Cases**:
- Request logging
- Performance monitoring
- Security checks
- Request transformation

#### AfterHandleRequest

**Triggered**: After HTTP request processing

```php
final readonly class AfterHandleRequest
{
    public function __construct(
        public Request $request,
        public Response $response,
        public Duration $processingTime,
        public Timestamp $timestamp,
        public array $context = []
    ) {}
}
```

**Use Cases**:
- Response logging
- Performance metrics
- Analytics collection
- Cleanup operations

### Example: User Registration Event

```php
namespace App\Domain\User\Events;

final readonly class UserRegisteredEvent
{
    public readonly Timestamp $occurredAt;

    public function __construct(
        public UserId $userId,
        public Email $email,
        public UserName $userName,
        ?Timestamp $occurredAt = null
    ) {
        $this->occurredAt = $occurredAt ?? Timestamp::now();
    }
}

// Dispatching the event
final readonly class UserService
{
    public function __construct(
        private UserRepository $repository,
        private EventBus $eventBus
    ) {}

    public function register(RegisterUserCommand $command): User
    {
        $user = User::create(
            $command->email,
            $command->userName,
            $command->password
        );

        $this->repository->save($user);

        // Dispatch domain event
        $this->eventBus->dispatch(
            new UserRegisteredEvent(
                $user->id,
                $user->email,
                $user->userName
            )
        );

        return $user;
    }
}
```

## Event Handler Registration

### Attribute-Based Registration

**Primary Method**: Use `#[OnEvent]` attribute for automatic discovery

```php
use App\Framework\Core\Events\OnEvent;

final readonly class UserEventHandlers
{
    public function __construct(
        private EmailService $emailService,
        private Logger $logger
    ) {}

    #[OnEvent(priority: 100)]
    public function sendWelcomeEmail(UserRegisteredEvent $event): void
    {
        $this->emailService->send(
            to: $event->email,
            template: 'welcome',
            data: ['userName' => $event->userName->value]
        );
    }

    #[OnEvent(priority: 50)]
    public function logUserRegistration(UserRegisteredEvent $event): void
    {
        $this->logger->info('User registered', [
            'user_id' => $event->userId->toString(),
            'email' => $event->email->value,
            'occurred_at' => $event->occurredAt->format('Y-m-d H:i:s')
        ]);
    }
}
```

**Attribute Parameters**:
- `priority` (optional): Handler execution order (higher = earlier, default: 0)
- `stopPropagation` (optional): Stop execution after this handler (default: false)

### Priority-Based Execution

Handlers execute in **priority order** (highest to lowest):

```php
#[OnEvent(priority: 200)]  // Executes first
public function criticalHandler(OrderCreatedEvent $event): void { }

#[OnEvent(priority: 100)]  // Executes second
public function highPriorityHandler(OrderCreatedEvent $event): void { }

#[OnEvent(priority: 50)]   // Executes third
public function normalHandler(OrderCreatedEvent $event): void { }

#[OnEvent]                 // Executes last (default priority: 0)
public function defaultHandler(OrderCreatedEvent $event): void { }
```

**Use Cases for Priorities**:
- **Critical (200+)**: Security checks, validation, fraud detection
- **High (100-199)**: Transaction logging, audit trail
- **Normal (50-99)**: Business logic, notifications
- **Low (0-49)**: Analytics, metrics, cleanup

### Stop Propagation

**Purpose**: Prevent subsequent handlers from executing

```php
#[OnEvent(priority: 100, stopPropagation: true)]
public function validatePayment(PaymentProcessingEvent $event): PaymentValidationResult
{
    $result = $this->validator->validate($event->payment);

    if (!$result->isValid()) {
        // Stop propagation - no further handlers execute
        return new PaymentValidationFailure($result->errors);
    }

    return new PaymentValidationSuccess();
}

#[OnEvent(priority: 50)]
public function processPayment(PaymentProcessingEvent $event): void
{
    // This handler only executes if validation passes
    // (previous handler didn't set stopPropagation result)
    $this->gateway->charge($event->payment);
}
```

**Important**: `stopPropagation` stops execution **after** the current handler completes.

### Manual Handler Registration

**Alternative**: Register handlers programmatically

```php
final readonly class EventInitializer
{
    public function __construct(
        private EventDispatcher $dispatcher
    ) {}

    #[Initializer]
    public function registerHandlers(): void
    {
        // Using listen() method
        $this->dispatcher->listen(
            OrderCreatedEvent::class,
            function (OrderCreatedEvent $event) {
                // Handle event
            },
            priority: 100
        );

        // Using addHandler() method
        $this->dispatcher->addHandler(
            eventClass: UserRegisteredEvent::class,
            handler: [$this->userService, 'onUserRegistered'],
            priority: 50
        );
    }
}
```

**When to use manual registration**:
- Dynamic handler registration based on configuration
- Third-party library integration
- Closure-based handlers for simple cases
- Runtime handler modification

### Handler Discovery

The framework automatically discovers handlers marked with `#[OnEvent]`:

```php
// In Application Bootstrap
final readonly class EventSystemInitializer
{
    #[Initializer]
    public function initialize(EventDispatcher $dispatcher): void
    {
        // Automatic discovery finds all #[OnEvent] methods
        $discoveredHandlers = $this->attributeScanner->findMethodsWithAttribute(
            OnEvent::class
        );

        foreach ($discoveredHandlers as $handler) {
            $dispatcher->addHandler(
                eventClass: $handler->getEventClass(),
                handler: [$handler->instance, $handler->method],
                priority: $handler->attribute->priority ?? 0
            );
        }
    }
}
```

**No Manual Setup Required**: Framework handles discovery automatically during initialization.

## Event Middleware

**Concept**: Middleware pattern for event processing (transform, filter, log, etc.)

### Custom Event Middleware

```php
interface EventMiddleware
{
    public function process(object $event, callable $next): mixed;
}

final readonly class LoggingEventMiddleware implements EventMiddleware
{
    public function __construct(
        private Logger $logger
    ) {}

    public function process(object $event, callable $next): mixed
    {
        $eventClass = get_class($event);
        $startTime = microtime(true);

        $this->logger->debug("Event dispatched: {$eventClass}");

        try {
            $result = $next($event);

            $duration = (microtime(true) - $startTime) * 1000;
            $this->logger->debug("Event processed: {$eventClass}", [
                'duration_ms' => $duration
            ]);

            return $result;
        } catch (\Throwable $e) {
            $this->logger->error("Event failed: {$eventClass}", [
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }
}
```

### Middleware Pipeline

```php
final class EventDispatcherWithMiddleware
{
    /** @var EventMiddleware[] */
    private array $middleware = [];

    public function addMiddleware(EventMiddleware $middleware): void
    {
        $this->middleware[] = $middleware;
    }

    public function dispatch(object $event): array
    {
        $pipeline = array_reduce(
            array_reverse($this->middleware),
            fn($next, $middleware) => fn($event) => $middleware->process($event, $next),
            fn($event) => $this->eventDispatcher->dispatch($event)
        );

        return $pipeline($event);
    }
}
```

### Validation Middleware

```php
final readonly class ValidationEventMiddleware implements EventMiddleware
{
    public function __construct(
        private ValidatorInterface $validator
    ) {}

    public function process(object $event, callable $next): mixed
    {
        // Validate event before dispatching
        $errors = $this->validator->validate($event);

        if (!empty($errors)) {
            throw new InvalidEventException(
                "Event validation failed: " . implode(', ', $errors)
            );
        }

        return $next($event);
    }
}
```

## Async Event Processing

### Queue-Based Async Handling

```php
use App\Framework\Queue\Queue;
use App\Framework\Queue\ValueObjects\JobPayload;

final readonly class AsyncEventHandler
{
    public function __construct(
        private Queue $queue
    ) {}

    #[OnEvent(priority: 50)]
    public function processAsync(OrderCreatedEvent $event): void
    {
        // Dispatch to queue for async processing
        $job = new ProcessOrderEmailJob(
            orderId: $event->orderId,
            userEmail: $event->userEmail
        );

        $this->queue->push(
            JobPayload::immediate($job)
        );
    }
}

// Background Job
final readonly class ProcessOrderEmailJob
{
    public function __construct(
        public OrderId $orderId,
        public Email $userEmail
    ) {}

    public function handle(EmailService $emailService): void
    {
        // Process email asynchronously
        $emailService->sendOrderConfirmation(
            $this->orderId,
            $this->userEmail
        );
    }
}
```

### Event Buffering Pattern

```php
final class EventBuffer
{
    private array $bufferedEvents = [];

    #[OnEvent]
    public function bufferEvent(DomainEvent $event): void
    {
        $this->bufferedEvents[] = $event;
    }

    public function flush(EventDispatcher $dispatcher): void
    {
        foreach ($this->bufferedEvents as $event) {
            $dispatcher->dispatch($event);
        }

        $this->bufferedEvents = [];
    }
}

// Usage in transaction
try {
    $this->entityManager->beginTransaction();

    // Buffer events during transaction
    $this->eventBuffer->bufferEvent(new OrderCreatedEvent(/* ... */));
    $this->eventBuffer->bufferEvent(new InventoryReservedEvent(/* ... */));

    $this->entityManager->commit();

    // Flush events after successful commit
    $this->eventBuffer->flush($this->dispatcher);
} catch (\Exception $e) {
    $this->entityManager->rollback();
    // Events are not flushed on rollback
    throw $e;
}
```

## Event Best Practices

### 1. Event Naming

**✅ Good**: Past tense, descriptive
```php
OrderCreatedEvent
UserRegisteredEvent
PaymentCompletedEvent
InventoryReservedEvent
```

**❌ Bad**: Present/future tense, vague
```php
CreateOrderEvent
RegisterUserEvent
CompletePayment
ReserveInventory
```

### 2. Event Granularity

**✅ Focused Events**:
```php
final readonly class OrderCreatedEvent { /* ... */ }
final readonly class OrderShippedEvent { /* ... */ }
final readonly class OrderCancelledEvent { /* ... */ }
```

**❌ God Events**:
```php
final readonly class OrderEvent
{
    public function __construct(
        public string $action, // 'created', 'shipped', 'cancelled'
        public Order $order
    ) {}
}
```

### 3. Immutability

**✅ Readonly Events**:
```php
final readonly class UserUpdatedEvent
{
    public function __construct(
        public UserId $userId,
        public Email $newEmail
    ) {}
}
```

**❌ Mutable Events**:
```php
final class UserUpdatedEvent
{
    public UserId $userId;
    public Email $newEmail;

    public function setEmail(Email $email): void
    {
        $this->newEmail = $email; // Bad: Events should be immutable
    }
}
```

### 4. Handler Independence

**✅ Independent Handlers**:
```php
#[OnEvent]
public function sendEmail(OrderCreatedEvent $event): void
{
    // Self-contained - doesn't depend on other handlers
    $this->emailService->send(/* ... */);
}

#[OnEvent]
public function updateInventory(OrderCreatedEvent $event): void
{
    // Independent - no shared state with email handler
    $this->inventoryService->reserve(/* ... */);
}
```

**❌ Coupled Handlers**:
```php
private bool $emailSent = false;

#[OnEvent(priority: 100)]
public function sendEmail(OrderCreatedEvent $event): void
{
    $this->emailService->send(/* ... */);
    $this->emailSent = true; // Bad: Shared state
}

#[OnEvent(priority: 50)]
public function logEmailSent(OrderCreatedEvent $event): void
{
    if ($this->emailSent) { // Bad: Depends on other handler
        $this->logger->info('Email sent');
    }
}
```

### 5. Error Handling

**✅ Graceful Degradation**:
```php
#[OnEvent]
public function sendNotification(OrderCreatedEvent $event): void
{
    try {
        $this->notificationService->send(/* ... */);
    } catch (NotificationException $e) {
        // Log error but don't fail the entire event dispatch
        $this->logger->error('Notification failed', [
            'order_id' => $event->orderId->toString(),
            'error' => $e->getMessage()
        ]);
    }
}
```

### 6. Avoid Circular Events

**❌ Circular Dependency**:
```php
#[OnEvent]
public function onOrderCreated(OrderCreatedEvent $event): void
{
    // Bad: Dispatching event from event handler can cause loops
    $this->eventBus->dispatch(new OrderProcessedEvent($event->orderId));
}

#[OnEvent]
public function onOrderProcessed(OrderProcessedEvent $event): void
{
    // Bad: Creates circular dependency
    $this->eventBus->dispatch(new OrderCreatedEvent(/* ... */));
}
```

**✅ Use Command/Service Layer**:
```php
#[OnEvent]
public function onOrderCreated(OrderCreatedEvent $event): void
{
    // Good: Use service to perform additional work
    $this->orderProcessingService->processOrder($event->orderId);
}
```

### 7. Performance Considerations

**Heavy Operations → Queue**:
```php
#[OnEvent]
public function generateInvoicePdf(OrderCreatedEvent $event): void
{
    // Heavy operation - push to queue
    $this->queue->push(
        JobPayload::immediate(
            new GenerateInvoiceJob($event->orderId)
        )
    );
}
```

**Light Operations → Synchronous**:
```php
#[OnEvent]
public function logOrderCreation(OrderCreatedEvent $event): void
{
    // Light operation - execute immediately
    $this->logger->info('Order created', [
        'order_id' => $event->orderId->toString()
    ]);
}
```

## Common Patterns

### Event Sourcing Pattern

```php
final class OrderEventStore
{
    #[OnEvent]
    public function appendEvent(DomainEvent $event): void
    {
        $this->eventStore->append([
            'event_type' => get_class($event),
            'event_data' => json_encode($event),
            'occurred_at' => $event->occurredAt->format('Y-m-d H:i:s'),
            'aggregate_id' => $event->getAggregateId()
        ]);
    }
}
```

### Saga Pattern

```php
final readonly class OrderSaga
{
    #[OnEvent(priority: 100)]
    public function onOrderCreated(OrderCreatedEvent $event): void
    {
        // Step 1: Reserve inventory
        $this->inventoryService->reserve($event->items);
    }

    #[OnEvent(priority: 90)]
    public function onInventoryReserved(InventoryReservedEvent $event): void
    {
        // Step 2: Process payment
        $this->paymentService->charge($event->orderId);
    }

    #[OnEvent(priority: 80)]
    public function onPaymentCompleted(PaymentCompletedEvent $event): void
    {
        // Step 3: Ship order
        $this->shippingService->ship($event->orderId);
    }
}
```

### CQRS Pattern

```php
// Command Side - Dispatches Events
final readonly class CreateOrderHandler
{
    public function handle(CreateOrderCommand $command): Order
    {
        $order = Order::create($command);
        $this->repository->save($order);

        $this->eventBus->dispatch(
            new OrderCreatedEvent($order)
        );

        return $order;
    }
}

// Query Side - Maintains Read Model
#[OnEvent]
public function updateOrderReadModel(OrderCreatedEvent $event): void
{
    $this->orderReadModel->insert([
        'order_id' => $event->orderId->toString(),
        'user_id' => $event->userId->toString(),
        'total' => $event->total->toDecimal(),
        'status' => 'created'
    ]);
}
```

## Framework Integration

### With Queue System

```php
#[OnEvent]
public function queueHeavyTask(OrderCreatedEvent $event): void
{
    $this->queue->push(
        JobPayload::immediate(
            new ProcessOrderAnalyticsJob($event->orderId)
        )
    );
}
```

### With Scheduler

```php
#[OnEvent]
public function scheduleReminder(OrderCreatedEvent $event): void
{
    $this->scheduler->schedule(
        'send-order-reminder-' . $event->orderId,
        OneTimeSchedule::at(Timestamp::now()->addDays(3)),
        fn() => $this->emailService->sendReminder($event->orderId)
    );
}
```

### With Cache System

```php
#[OnEvent]
public function invalidateCache(OrderCreatedEvent $event): void
{
    $this->cache->forget(
        CacheTag::fromString("user_{$event->userId}")
    );
}
```

## Summary

The Event System provides:
- ✅ **Decoupled Architecture**: Loose coupling via events
- ✅ **Flexible Handling**: Priority-based, propagation control
- ✅ **Automatic Discovery**: `#[OnEvent]` attribute registration
- ✅ **Multiple Patterns**: Domain events, application events, async processing
- ✅ **Framework Integration**: Works with Queue, Scheduler, Cache
- ✅ **Testing Support**: Easy to unit test and integration test
- ✅ **Performance**: Efficient handler execution with priority optimization

**When to Use Events**:
- Decoupling domain logic from side effects (email, notifications)
- Cross-module communication without tight coupling
- Audit trail and event sourcing
- Async processing of non-critical operations
- CQRS and saga patterns

**When NOT to Use Events**:
- Synchronous business logic requiring immediate results
- Simple function calls (don't over-engineer)
- Performance-critical paths (events have overhead)
- Operations requiring transactional guarantees across handlers
