Query Bus
The Query Bus is another optional component in the Event Sourcing library that coordinates the data flow in the system. Unlike the command bus, the query bus intention is not to perform actions on the system but instead retrieve information from the system. It allows to fully utilize the read write split and the usage of small, independent and tailored projections.
Query
First, you need to create a simple data transfer object which will be our query. It represent our intention to retrieve data from the system.
Handler
The next step is to create a handler which has a method which can handle the query. The method will be marked with the
#[Answer]
attribute. The method will be called when the query is dispatched in the bus and should return the desired
data.
final class QueryProfileHandler
{
#[Answer]
public function __invoke(QueryProfile $query): mixed
{
return 'result';
}
}
Warning
A query can only be answered by one method.
Note
To use Service Handler you need to register the handler in the ServiceHandlerProvider
.
Tip
A class can have multiple methods which answers different queries.
Projector
Another way to handle queries is to answer them directly in the corresponding projectors. The configuration is the same
as when using a dedicated class. The method which should handle the query will be marked with the #[Answer]
attribute.
use Patchlevel\EventSourcing\Attribute\Projector;
#[Projector('profiles')]
final class ProfileProjector
{
#[Answer]
public function answerQueryProfile(QueryProfile $query): mixed
{
return 'result';
}
// projector related methods to maintain the state of profiles
}
Tip
Using small dedicated projections for each usecase is best practice. Using them directly as query handlers are endoresed and can reduce fragmentation of the system.
Setup
We provide a SyncQueryBus
that you can use to dispatch queries.
You need to pass a HandlerProvider
to the constructor.
use Patchlevel\EventSourcing\QueryBus\HandlerProvider;
use Patchlevel\EventSourcing\QueryBus\SyncQueryBus;
/** @var HandlerProvider $handlerProvider */
$queryBus = new SyncQueryBus($handlerProvider);
$result = $queryBus->dispatch(new QueryProfile($profileId));
Provider
We created an interface for HandlerProvider
which allows you to implement different types of providers that you can
use to register handlers. Right now we have two implementations: ServiceHandlerProvider
and ChainHandlerProvider
.
Service Handler Provider
The ServiceHandlerProvider
is used to handle queries by invoking methods on services.
use Patchlevel\EventSourcing\QueryBus\ServiceHandlerProvider;
$provider = new ServiceHandlerProvider([
new QueryProfileHandler(),
new ProfileProjector(
$dbalConnection,
),
]);
Chain Handler Provider
The ChainHandlerProvider
allows you to combine multiple handler providers.
use Patchlevel\EventSourcing\QueryBus\ChainHandlerProvider;
$provider = new ChainHandlerProvider([
$serviceHandlerProvider1,
$serviceHandlerProvider2,
]);