This library contains a base implementation for CQRS/Types.
- Installation
- Queries
- Commands
- ProfilerBag
composer require lmc/cqrs-handler
Base implementation for a Query Fetcher Interface (see Types/QueryFetcherInterface).
It is responsible for
- finding a Query Handler based on Query request type
- handle all Query features
- caching
- requires an instance of
Psr\Cache\CacheItemPoolInterface
- requires an instance of
- profiling
- requires an instance of
Lmc\Cqrs\Handler\ProfilerBag
- requires an instance of
- caching
- decoding a response from the Query Handler
If you are not using a CQRS/Bundle you need to set up a Query Fetcher yourself.
Minimal Initialization
$queryFetcher = new QueryFetcher(
// Cache
false, // disabled cache
null, // no cache pool -> no caching
// Profiling
null // no profiler bag -> no profiling
);
Full Initialization with all features.
$profilerBag = new ProfilerBag();
$queryFetcher = new QueryFetcher(
// Cache
true, // is cache enabled
$cache, // instance of Psr\Cache\CacheItemPoolInterface
// Profiling
$profilerBag, // collection of profiled information
// Custom handlers
// NOTE: there is multiple ways of defining handler
[
[new TopMostHandler(), PrioritizedItem::PRIORITY_HIGHEST], // Array definition of priority
new OtherHandler(), // Used with default priority of 50
new PrioritizedItem(new FallbackHandler(), PrioritizedItem::PRIORITY_LOWEST) // PrioritizedItem value object definition
],
// Custom response decoders
// NOTE: there is multiple ways of defining response decoders
[
[new TopMostDecoder(), PrioritizedItem::PRIORITY_HIGHEST], // Array definition of priority
new OtherDecoder(), // Used with default priority of 50
new PrioritizedItem(new FallbackDecoder(), PrioritizedItem::PRIORITY_LOWEST) // PrioritizedItem value object definition
]
);
You can add handlers and decoders by add
methods.
$this->queryFetcher->addHandler(new MyQueryHandler(), PrioritizedItem::PRIORITY_MEDIUM);
$this->queryFetcher->addDecoder(new MyQueryResponseDecoder(), PrioritizedItem::PRIORITY_HIGH);
Fetching a query
You can do whatever you want with a response, we will persist a result into db, for an example or log an error.
// with continuation
$this->queryFetcher->fetch(
$query,
fn ($response) => $this->repository->save($response),
fn (\Throwable $error) => $this->logger->critical($error->getMassage())
);
// with return
try {
$response = $this->queryFetcher->fetchAndReturn($query);
$this->repository->save($response);
} catch (\Throwable $error) {
$this->logger->critical($error->getMessage());
}
It is responsible for handling a specific Query request and passing a result into OnSuccess
callback. See more here.
This handler is automatically created QueryFetcher
and added amongst handlers with priority 80
when an instance of CacheItemPoolInterface
is passed into QueryFetcher
.
It supports queries implementing CacheableInterface
with cacheTime > 0
. The second condition allows you to avoid caching in queries with CacheableInterface
by just a cache time value.
There is also CacheTime::noCache()
named constructor to make it explicit.
It handles a query by retrieving a result out of a cache (if the cache has the item and is hit
(see PSR-6 for more).
This handler supports a query with request type of "callable"
, "Closure"
or "callback"
(which all stands for a callable
request).
It simply calls a created request as a function and returns a result to OnSuccess
callback.
Query is a request which fetch a data without changing anything. See more here
This is a predefined implementation for a Query with CacheableInterface
.
It is handy for in-app queries where you want to use cache for a result. You can also extend it and add more features.
$query = new CallbackQueryHandler(
fn () => $this->repository->fetchData(),
new CacheKey('my-data-key'),
CacheTime::oneHour()
);
This is a predefined implementation for a Query with CacheableInterface
and ProfileableInterface
.
It is handy for in-app queries where you want to use cache for a result and also profile it. You can also extend it and add more features.
$query = new ProfiledCallbackQueryHandler(
fn () => $this->repository->fetchData(),
new CacheKey('my-data-key'),
CacheTime::oneHour(),
'my-profiler-key',
['additional' => 'data'] // optional
);
Base implementation for a Command Sender Interface (see Types/CommandSenderInterface).
It is responsible for
- finding a Send Command Handler based on Command request type
- handle all Command features
- profiling
- requires an instance of
Lmc\Cqrs\Handler\ProfilerBag
- requires an instance of
- profiling
- decoding a response from the Send Command Handler
If you are not using a CQRS/Bundle you need to set up a Command Sender yourself.
Minimal Initialization
$commandSender = new CommandSender(
// Profiling
null // no profiler bag -> no profiling
);
Full Initialization with all features.
$profilerBag = new ProfilerBag();
$commandSender = new CommandSender(
// Profiling
$profilerBag, // collection of profiled information
// Custom handlers
// NOTE: there is multiple ways of defining handler
[
[new TopMostHandler(), PrioritizedItem::PRIORITY_HIGHEST], // Array definition of priority
new OtherHandler(), // Used with default priority of 50
new PrioritizedItem(new FallbackHandler(), PrioritizedItem::PRIORITY_LOWEST) // PrioritizedItem value object definition
],
// Custom response decoders
// NOTE: there is multiple ways of defining response decoders
[
[new TopMostDecoder(), PrioritizedItem::PRIORITY_HIGHEST], // Array definition of priority
new OtherDecoder(), // Used with default priority of 50
new PrioritizedItem(new FallbackDecoder(), PrioritizedItem::PRIORITY_LOWEST) // PrioritizedItem value object definition
]
);
You can add handlers and decoders by add
methods.
$this->commandSender->addHandler(new MyCommandHandler(), PrioritizedItem::PRIORITY_MEDIUM);
$this->commandSender->addDecoder(new MyCommandResponseDecoder(), PrioritizedItem::PRIORITY_HIGH);
Sending a command
You can do whatever you want with a response, we will persist a result into db, for an example or log an error.
// with continuation
$this->commandSender->send(
$command,
fn ($response) => $this->repository->save($response),
fn (\Throwable $error) => $this->logger->critical($error->getMassage())
);
// with return
try {
$response = $this->commandSender->sendAndReturn($query);
$this->repository->save($response);
} catch (\Throwable $error) {
$this->logger->critical($error->getMessage());
}
It is responsible for handling a specific Command request and passing a result into OnSuccess
callback. See more here.
This handler supports a command with request type of "callable"
, "Closure"
or "callback"
(which all stands for a callable
request).
It simply calls a created request as a function and returns a result to OnSuccess
callback.
Command is a request which change a data and may return result data. See more here
This is a predefined implementation for a Command with ProfileableInterface
.
It is handy for in-app commands where you want to profile it. You can also extend it and add more features.
$command = new ProfiledDataCommand(
fn () => $this->repository->fetchData(),
new CacheKey('my-data-key'),
CacheTime::oneHour(),
'my-profiler-key',
['additional' => 'data'] // optional
);
Service, which is a collection of all profiler information in the current request.
If you pass it to the QueryFetcher
or CommandSender
, they will profile query/command implementing ProfileableInterface
to the ProfilerBag
.
The information inside are used by a CqrsDataCollector
, which shows them in the Symfony profiler (used in CQRS/Bundle).
Profiler bag can also hold an information about a verbosity level of profiling.
Levels:
- NORMAL =
empty value
(default) - VERBOSE =
'verbose'
- DEBUG =
'debug'
There might be additional data added to the ProfilerItem
with higher levels of verbosity.
You can set it by
$profilerBag->setVerbosity(ProfilerBag::VERBOSITY_VERBOSE);