# Events Package Specification ## Overview The Events package provides a robust event system that matches Laravel's event functionality while leveraging Dart's async capabilities. It integrates with our Queue, Bus, and Database packages to provide a complete event handling solution. > **Related Documentation** > - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status > - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns > - See [Testing Guide](testing_guide.md) for testing approaches > - See [Getting Started Guide](getting_started.md) for development setup > - See [Contracts Package Specification](contracts_package_specification.md) for event contracts ## Core Features ### 1. Event Dispatcher ```dart /// Core event dispatcher implementation class EventDispatcher implements EventDispatcherContract { final Container _container; final Map> _listeners = {}; final List _subscribers = {}; final QueueContract? _queue; final BroadcasterContract? _broadcaster; final List _afterCommitEvents = []; EventDispatcher( this._container, { QueueContract? queue, BroadcasterContract? broadcaster }) : _queue = queue, _broadcaster = broadcaster; @override void listen(void Function(T event) listener) { _listeners.putIfAbsent(T, () => []).add( EventListener(listener) ); } @override Future dispatch(T event) async { var listeners = _listeners[T] ?? []; // Handle after commit events if (event is ShouldDispatchAfterCommit && _isWithinTransaction()) { _afterCommitEvents.add(event); return; } // Handle queued events if (event is ShouldQueue && _queue != null) { await _queueEvent(event, listeners); return; } // Handle broadcasting if (event is ShouldBroadcast && _broadcaster != null) { await _broadcastEvent(event); } // Notify listeners await _notifyListeners(event, listeners); } @override Future dispatchAfterCommit(T event) async { if (_isWithinTransaction()) { _afterCommitEvents.add(event); } else { await dispatch(event); } } bool _isWithinTransaction() { if (_container.has()) { var db = _container.make(); return db.transactionLevel > 0; } return false; } Future _dispatchAfterCommitEvents() async { var events = List.from(_afterCommitEvents); _afterCommitEvents.clear(); for (var event in events) { await dispatch(event); } } } ``` ### 2. Event Discovery ```dart /// Discovers event handlers through reflection and attributes class EventDiscovery { final Container _container; final Reflector _reflector; EventDiscovery(this._container, this._reflector); /// Discovers event handlers in a directory Future discoverEvents(String path) async { var files = Directory(path).listSync(recursive: true); for (var file in files) { if (file.path.endsWith('.dart')) { await _processFile(file.path); } } } Future _processFile(String path) async { var library = await _reflector.loadLibrary(path); for (var type in library.declarations.values) { if (type is ClassMirror) { _processClass(type); } } } void _processClass(ClassMirror classMirror) { // Find @Handles annotations for (var method in classMirror.declarations.values) { if (method is MethodMirror) { var handles = method.metadata .firstWhere((m) => m.type == Handles, orElse: () => null); if (handles != null) { var eventType = handles.getField('event').reflectee; _registerHandler(classMirror.reflectedType, method.simpleName, eventType); } } } } void _registerHandler(Type classType, Symbol methodName, Type eventType) { var instance = _container.make(classType); var dispatcher = _container.make(); dispatcher.listen(eventType, (event) { var mirror = reflect(instance); mirror.invoke(methodName, [event]); }); } } ``` ### 3. Event Broadcasting ```dart /// Contract for event broadcasters abstract class BroadcasterContract { /// Broadcasts an event Future broadcast( List channels, String eventName, dynamic data ); /// Creates a private channel Channel privateChannel(String name); /// Creates a presence channel PresenceChannel presenceChannel(String name); } /// Pusher event broadcaster class PusherBroadcaster implements BroadcasterContract { final PusherClient _client; final AuthManager _auth; PusherBroadcaster(this._client, this._auth); @override Future broadcast( List channels, String eventName, dynamic data ) async { for (var channel in channels) { await _client.trigger(channel, eventName, data); } } @override Channel privateChannel(String name) { return PrivateChannel(_client, _auth, name); } @override PresenceChannel presenceChannel(String name) { return PresenceChannel(_client, _auth, name); } } ``` ### 4. Integration with Queue ```dart /// Job for processing queued events class QueuedEventJob implements Job { final dynamic event; final List listeners; final Map data; QueuedEventJob({ required this.event, required this.listeners, this.data = const {} }); @override Future handle() async { for (var listener in listeners) { try { await listener.handle(event); } catch (e) { await _handleFailure(e); } } } @override Future failed([Exception? e]) async { if (event is FailedEventHandler) { await (event as FailedEventHandler).failed(e); } } @override int get tries => event is HasTries ? (event as HasTries).tries : 1; @override Duration? get timeout => event is HasTimeout ? (event as HasTimeout).timeout : null; } ``` ### 5. Integration with Bus ```dart /// Event command for command bus integration class EventCommand implements Command { final dynamic event; final List listeners; EventCommand(this.event, this.listeners); @override Type get handler => EventCommandHandler; } /// Handler for event commands class EventCommandHandler implements Handler { final EventDispatcher _events; EventCommandHandler(this._events); @override Future handle(EventCommand command) async { await _events._notifyListeners( command.event, command.listeners ); } } ``` ## Usage Examples ### Basic Event Handling ```dart // Define event class OrderShipped { final Order order; OrderShipped(this.order); } // Create listener @Handles(OrderShipped) class OrderShippedListener { void handle(OrderShipped event) { // Handle event } } // Register and dispatch dispatcher.listen((event) { // Handle event }); await dispatcher.dispatch(OrderShipped(order)); ``` ### After Commit Events ```dart class OrderCreated implements ShouldDispatchAfterCommit { final Order order; OrderCreated(this.order); } // In transaction await db.transaction((tx) async { var order = await tx.create(orderData); await dispatcher.dispatchAfterCommit(OrderCreated(order)); }); ``` ### Broadcasting ```dart class MessageSent implements ShouldBroadcast { final Message message; @override List broadcastOn() => [ 'private-chat.${message.roomId}' ]; @override Map get broadcastWith => { 'id': message.id, 'content': message.content, 'user': message.user.toJson() }; } // Create private channel var channel = broadcaster.privateChannel('chat.123'); await channel.whisper('typing', {'user': 'john'}); ``` ### Queue Integration ```dart class ProcessOrder implements ShouldQueue { final Order order; @override String get queue => 'orders'; @override int get tries => 3; @override Duration get timeout => Duration(minutes: 5); } // Dispatch queued event await dispatcher.dispatch(ProcessOrder(order)); ``` ## Testing ```dart void main() { group('Event Dispatcher', () { test('dispatches after commit', () async { var dispatcher = MockEventDispatcher(); var db = MockDatabase(); await db.transaction((tx) async { await dispatcher.dispatchAfterCommit(OrderShipped(order)); expect(dispatcher.hasAfterCommitEvents, isTrue); }); expect(dispatcher.hasAfterCommitEvents, isFalse); verify(() => dispatcher.dispatch(any())).called(1); }); test('discovers event handlers', () async { var discovery = EventDiscovery(container, reflector); await discovery.discoverEvents('lib/events'); var dispatcher = container.make(); await dispatcher.dispatch(OrderShipped(order)); verify(() => orderListener.handle(any())).called(1); }); }); } ``` ## Next Steps 1. Complete after commit handling 2. Enhance event discovery 3. Add more broadcast drivers 4. Improve queue integration 5. Add performance optimizations 6. Write more tests Would you like me to enhance any other package specifications? ## Development Guidelines ### 1. Getting Started Before implementing event features: 1. Review [Getting Started Guide](getting_started.md) 2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) 3. Follow [Testing Guide](testing_guide.md) 4. Use [Foundation Integration Guide](foundation_integration_guide.md) 5. Understand [Contracts Package Specification](contracts_package_specification.md) ### 2. Implementation Process For each event feature: 1. Write tests following [Testing Guide](testing_guide.md) 2. Implement following Laravel patterns 3. Document following [Getting Started Guide](getting_started.md#documentation) 4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md) ### 3. Quality Requirements All implementations must: 1. Pass all tests (see [Testing Guide](testing_guide.md)) 2. Meet Laravel compatibility requirements 3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md)) 4. Implement required contracts (see [Contracts Package Specification](contracts_package_specification.md)) ### 4. Integration Considerations When implementing events: 1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md) 2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) 3. Use testing approaches from [Testing Guide](testing_guide.md) 4. Follow development setup in [Getting Started Guide](getting_started.md) 5. Implement all contracts from [Contracts Package Specification](contracts_package_specification.md) ### 5. Performance Guidelines Event system must: 1. Handle high throughput efficiently 2. Minimize memory usage 3. Support async operations 4. Scale horizontally 5. Meet performance targets in [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md#performance-benchmarks) ### 6. Testing Requirements Event tests must: 1. Cover all event scenarios 2. Test async behavior 3. Verify queue integration 4. Check broadcasting 5. Follow patterns in [Testing Guide](testing_guide.md) ### 7. Documentation Requirements Event documentation must: 1. Explain event patterns 2. Show integration examples 3. Cover error handling 4. Include performance tips 5. Follow standards in [Getting Started Guide](getting_started.md#documentation)