lmc / cqrs-handler
A library containing handlers to help with Queries and Commands
Installs: 26 864
Dependents: 2
Suggesters: 1
Security: 0
Stars: 1
Watchers: 13
Forks: 0
Open Issues: 0
pkg:composer/lmc/cqrs-handler
Requires
- php: ^8.2
- ext-json: *
- ext-mbstring: *
- lmc/cqrs-types: ^3.2
- psr/cache: ^2.0 || ^3.0
- psr/http-message: ^1.0.1 || ^2.0
- ramsey/collection: ^1.2.2 || ^2.0
- ramsey/uuid: ^4.2.3
- symfony/stopwatch: ^5.0 || ^6.0 || ^7.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.5
- lmc/coding-standard: ^3.3
- php-parallel-lint/php-parallel-lint: ^1.2
- phpstan/extension-installer: ^1.1
- phpstan/phpstan: ^1.4
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^11.0.4
- symfony/cache: ^5.2 || ^6.0 || ^7.0
README
This library contains a base implementation for CQRS/Types.
Table of contents
- Installation
- Queries
- Commands
- ProfilerBag
Installation
composer require lmc/cqrs-handler
Query Fetcher
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
Usage
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()); }
Query Handlers
It is responsible for handling a specific Query request and passing a result into OnSuccess callback. See more here.
GetCachedHandler
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).
CallbackQueryHandler
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
Query is a request which fetch a data without changing anything. See more here
CachedDataQuery
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() );
ProfiledCachedDataQuery
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 );
Command Sender
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
Usage
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()); }
Send Command Handlers
It is responsible for handling a specific Command request and passing a result into OnSuccess callback. See more here.
CallbackSendCommandHandler
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
Command is a request which change a data and may return result data. See more here
ProfiledDataCommand
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 );
ProfilerBag
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).
Verbosity
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);