You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While the Event Store persists events, the Event Bus allows other parts of the application to react to these events *after* they have been successfully stored. This is crucial for decoupling components and enabling patterns like building read models (CQRS), triggering notifications, or starting long-running processes (Sagas).
3
+
An Event Bus is a central component responsible for dispatching [Domain Events](./domain-events.md) to interested [Event Listeners](./listeners.md).
4
4
5
-
## What is an Event Bus?
5
+
## Purpose
6
6
7
-
An Event Bus acts as a central dispatch mechanism for events. Components called [Event Listeners](./listeners.md) register their interest in specific types of events with the bus. When an event is published to the bus, the bus ensures it is delivered to all interested listeners.
7
+
The primary goal of an Event Bus is to **decouple event producers from event consumers (listeners)**. When an action occurs in your domain (e.g., a user registers, an order is placed), the code responsible for that action (e.g., a Command Handler) can publish the resulting event to the Event Bus without needing to know which specific components need to react to that event.
8
8
9
-
Key characteristics:
9
+
The Event Bus then routes the event to all registered listeners that are interested in that specific event type.
10
10
11
-
***Decoupling:** The publisher of an event doesn't need to know anything about the listeners, and listeners don't know about the publisher.
12
-
***Asynchronous Potential:** While implementations can be synchronous, event buses are often used for asynchronous processing, where listeners handle events in the background.
13
-
***Multiple Listeners:** A single event can be handled by zero, one, or many listeners.
14
-
15
-
## The `EventBus` Interface
16
-
17
-
Streak defines a simple interface for the event bus contract in `Streak\Domain\EventBus`:
11
+
## Key Interface (`Streak\Domain\EventBus`)
18
12
19
13
```php
20
14
<?php
@@ -24,131 +18,124 @@ namespace Streak\Domain;
24
18
interface EventBus
25
19
{
26
20
/**
27
-
* Publishes an event (typically wrapped in an Envelope) to interested listeners.
28
-
*
29
-
* @param Event\Envelope $event
21
+
* Registers a listener with the bus.
30
22
*/
31
-
public function publish(Event\Envelope $event): void;
23
+
public function add(Event\Listener $listener): void;
32
24
33
25
/**
34
-
* Subscribes a listener to specific events.
35
-
*
36
-
* Note: The exact mechanism for determining which events a listener handles
37
-
* often depends on the implementation (e.g., type hinting, attributes,
38
-
* explicit registration methods based on listensTo()).
39
-
*
40
-
* @param Event\Listener $listener
26
+
* Unregisters a listener from the bus.
41
27
*/
42
-
public function subscribe(Event\Listener $listener): void;
28
+
public function remove(Event\Listener $listener): void;
29
+
30
+
/**
31
+
* Publishes one or more event envelopes to interested listeners.
32
+
*/
33
+
public function publish(Event\Envelope ...$events);
43
34
}
44
35
```
45
36
46
-
## Event Bus vs. Subscriptions
47
-
48
-
It's important to distinguish the role of the `EventBus` from persistent [Subscriptions](./subscriptions.md):
37
+
***`add(Listener $listener)`:** Registers a listener instance with the bus.
38
+
***`remove(Listener $listener)`:** Removes a previously registered listener.
39
+
***`publish(Envelope ...$events)`:** Takes one or more `Event\Envelope` objects and dispatches them to the relevant registered listeners.
49
40
50
-
***Event Bus:** Primarily for *pushing* notifications about events that just happened. Good for immediate reactions, simple side effects, or triggering potentially transient listeners. Listeners rely on the bus for delivery.
51
-
***Subscriptions:** Primarily for *pulling* events from the `EventStore` in a reliable, stateful way. Ideal for critical tasks like building read models or running process managers that must process events exactly once and survive restarts. They manage their own progress independently of the bus.
41
+
## Registering Listeners
52
42
53
-
Often, the `PublishingEventStore` publishes events to the `EventBus`*and* these events are also processed independently later by `Subscriptions` pulling from the store.
43
+
Listeners must be registered with the Event Bus instance before they can receive events.
54
44
55
-
## Event Listeners
45
+
```php // Example Registration (Conceptual)
46
+
<?php
56
47
57
-
Event Listeners are components that react to specific domain events. They typically implement the `Streak\Domain\Event\Listener` interface.
48
+
use Streak\Domain\EventBus;
49
+
use Streak\Domain\Event\Listener;
58
50
59
-
```php
60
-
<?php
51
+
// Assume $bus is an EventBus instance and $listener is a Listener instance
61
52
62
-
namespace Streak\Domain\Event;
53
+
// Option 1: Simple Addition (Bus figures out interests)
54
+
$bus->add($listener); // The bus *might* inspect the listener or rely on configuration
55
+
// to know which events this listener handles.
63
56
64
-
interface Listener // Often combined with IdempotentListener
65
-
{
66
-
/**
67
-
* Called by the EventBus when a subscribed event occurs.
* @param Envelope $event The event envelope that failed.
77
-
* @param \Throwable $exception The exception caught during processing.
78
-
*/
79
-
// public function onError(Envelope $event, \Throwable $exception): void;
61
+
// Option 3: Tagging (In frameworks like Symfony)
62
+
// Services tagged with 'streak.event_listener' might be automatically registered.
63
+
// The framework extension might analyze the listener's method signatures (e.g., on<EventName>).
80
64
81
-
/**
82
-
* Allows the listener to specify which events it's interested in.
83
-
* Can return class names, etc., depending on the bus implementation.
84
-
*
85
-
* @return string[]|callable[]
86
-
*/
87
-
public function listensTo(): iterable;
88
-
}
65
+
// Option 4: Manual Dispatch (Less common for buses)
66
+
// $listener->on($eventEnvelope); // Bypasses the bus entirely
89
67
```
90
68
91
-
***`listensTo()`:** Specifies which event types (usually class names) the listener wants to receive. The Event Bus uses this information for routing.
92
-
***`on(Envelope $event)`:** The main method called by the bus when a subscribed event is published. It receives the full `Envelope` containing the event message and metadata.
93
-
***Idempotency:** Listeners should often be **idempotent**, meaning processing the same event multiple times should not result in incorrect state or unintended side effects. This is important because in distributed or asynchronous systems, events might be delivered more than once. Streak provides `Streak\Domain\Event\Listener\IdempotentListener` which usually involves tracking processed event IDs.
69
+
The exact registration mechanism depends on the specific `EventBus` implementation and potentially the framework integration (like the [Streak Symfony Bundle](../symfony-bundle/)). Some implementations might require explicit event type subscriptions, while others might introspect the listener (e.g., looking for `on<EventName>` methods or relying on framework tags).
// In a real app: send email, push notification, etc.
112
+
113
+
return true; // Event handled
114
+
}
115
+
116
+
return false; // Event ignored
130
117
}
131
118
}
132
119
```
120
+
***`on()`:** Contains the logic to handle the specific event (`UserRegistered` in this case). It checks the event type and returns `true` if handled.
121
+
***`Listener\Id` / `Identifying` Trait:** Included because listeners often need IDs, even if just for logging or potential future state management, although simpler transient listeners might omit it if the bus implementation allows.
133
122
134
-
## Implementations
123
+
## Event Bus Implementations
135
124
136
125
Streak core might provide basic synchronous implementations. More advanced implementations often integrate with message queuing systems (like RabbitMQ, Kafka) or use frameworks like Symfony Messenger.
137
126
138
-
***Synchronous:** The `publish` method directly calls `on` for all subscribed listeners within the same process and request. Simple but can slow down the initial request.
127
+
***Synchronous:** The `publish` method directly calls `on` for all registered listeners within the same process and request. Simple but can slow down the initial request.
139
128
***Asynchronous:** The `publish` method sends the event envelope to a message queue. Separate worker processes consume messages from the queue and invoke the appropriate listeners.
140
129
141
130
## Integration with Event Store
142
131
143
-
As mentioned in the Event Store documentation, the `Streak\Infrastructure\Domain\EventStore\PublishingEventStore` decorator is key. It ensures that `eventBus->publish()` is only called *after* the event has been successfully committed to the underlying persistent event store (`eventStore->add()`). This prevents listeners reacting via the bus to events that might ultimately fail to be persisted.
132
+
As mentioned in the [Event Store](./event-store.md) documentation, the `Streak\Infrastructure\Domain\EventStore\PublishingEventStore` decorator is key. It ensures that `eventBus->publish()` is only called *after* the event has been successfully committed to the underlying persistent event store (`eventStore->add()`). This prevents listeners reacting via the bus to events that might ultimately fail to be persisted.
144
133
145
-
## Configuration
134
+
## Event Bus vs. Subscriptions
146
135
147
-
Event Bus implementations and listener registration (subscribing listeners to the bus) are typically configured via dependency injection containers. The `StreakBundle` for Symfony automates the discovery and registration of services implementing `Event\Listener`with the configured Event Bus service.
136
+
While both Event Buses and [Subscriptions](./subscriptions.md) involve processing events with Listeners, they serve different primary purposes:
148
137
149
-
**Note on Direct Usage:** While the `EventBus` interface defines `publish` and `subscribe` methods, in many common Streak setups (especially using the `StreakBundle` and the `PublishingEventStore`), direct interaction with the event bus by the application developer is often minimal.
150
-
* The `PublishingEventStore` automatically handles calling `publish()` after events are successfully persisted.
151
-
* Listeners implementing `Streak\Domain\Event\Listener` are typically auto-discovered and subscribed to the bus by the framework's dependency injection container (autoconfiguration).
152
-
As a result, developers usually focus on creating the listener logic (the `on()` methods) and defining which events they `listenTo()`, rather than manually publishing events or subscribing listeners to the bus.
138
+
***Event Bus:** Focuses on **immediate, decoupled notification**. Ideal for simple side effects that need to happen *soon* after an event but don't necessarily require guaranteed, ordered processing relative to other events (e.g., sending a welcome email). Often asynchronous.
139
+
***Subscription:** Focuses on **reliable, persistent, often ordered processing** of the event stream, typically starting from a specific point. Essential for building stateful read models (Projections) or managing long-running business processes (Process Managers/Sagas) that depend on the sequence of events.
153
140
154
-
## Event Bus vs. Subscriptions
141
+
In many systems, both are used: an event is persisted to the Event Store, then published to the Event Bus for immediate notifications, *and* processed later by one or more Subscriptions for building read models or driving processes.
0 commit comments