zumba / cqrs
CQRS 库
Requires
- php: ^8.0
- zumba/symbiosis: ^3.0
Requires (Dev)
- phpstan/phpstan: ^0.12.90
- phpunit/phpunit: ^8.5 || ^9.5
- squizlabs/php_codesniffer: ^3.6
README
命令查询责任分离(CQRS)是一种成熟的设计模式。此库旨在实现创建 CQRS 命令和查询所需的 管道。
命令总线使用
命令总线允许带有处理器的命令被派发,结果可以是 成功
或 失败
。
按照设计,命令不应返回任何数据。
创建命令
创建派发到命令总线的命令 DTO 需要我们扩展抽象的 Command
类。
<?php namespace My\Command; use Zumba\CQRS\Command\Command; use Zumba\CQRS\Command\WithProperties; final class MyCommand extends Command implements WithProperties { protected string $message; public static function fromArray(array $props): Command { $command = new static(); $command->message = $props['message']; return $command; } }
创建命令处理器
使用从创建命令部分创建的 MyCommand
,我们需要处理此命令以执行操作。
提示:命令处理器是引入任何依赖的好地方。
<?php namespace My\Command; use Zumba\CQRS\Command\Command; use Zumba\CQRS\Command\CommandResponse; use Zumba\CQRS\Command\Handler; use Zumba\CQRS\CommandService; final class MyCommandHandler implements Handler { public function handle(Command $command, CommandService $commandService): CommandResponse { echo $command->message; return CommandResponse::fromSuccess(); } }
派发命令
创建了一个命令及其处理器后,我们就可以将命令派发到命令总线了。
可以实例化一个默认的“电池组”命令总线,将命令 DTO 派发到相应的处理器。
<?php use My\Command\MyCommand; use Zumba\CQRS\CommandBus; use Zumba\CQRS\Response\Success; $commandBus = CommandBus::defaultBus(); $command = MyCommand::fromProperties([ 'message' => 'Hello!', ]); $result = $commandBus->dispatch($command); // Hello! is echoed var_dump($result instanceof Success); // true
查询总线使用
与命令总线类似,查询总线使用查询 DTO 和相应的处理器派发到查询总线,以获取结果数据。
创建查询
创建派发到查询总线的查询 DTO 需要我们扩展抽象的 Query
类。
<?php namespace My\Query; use Zumba\CQRS\Query\Query; use Zumba\CQRS\Query\WithProperties; final class MyQuery extends Query implements WithProperties { protected string $ID; public static function fromArray(array $props): Query { $query = new static(); $query->ID = $props['id']; return $query; } }
创建查询处理器
查询处理器根据要返回的数据类型返回查询响应。
在上面的 MyQuery
DTO 中,假设这是检索特定实体的键值结果。我们会使用 Map
响应类型构建响应
<?php namespace My\Query; use Zumba\CQRS\Query\Handler; use Zumba\CQRS\Query\Query; use Zumba\CQRS\Query\QueryResponse; final class MyQueryHandler implements Handler { public function handle(Query $query): QueryResponse { // Do appropriate lookups return QueryResponse::fromMap([ 'id' => 'example-id', 'name' => 'example-name', ]); } }
派发查询
与派发命令类似,我们可以使用“电池组”查询总线,该总线可以使用 QueryBusTrait
包含。
namespace My\Query; use Zumba\CQRS\QueryBusTrait; class Example { use QueryBusTrait; public function query(): array { $response = $this->queryBus()->dispatch(new MyQuery([ 'id' => 'example-id', ])); return [ 'id' => $response['id'], 'name' => $response['name'], ]; } } var_dump((new Example)->query()); // [ // 'id' => 'example-id', // 'name' => 'example-name', // ]
提供者
提供者允许命令总线为派发到总线的 DTO 提供处理器。
zumba\cqrs
默认提供了一些提供者。
类提供者
Zumba\CQRS\Provider\ClassProvider
可以用于通过处理器工厂构建 DTO 的处理器。这可以在处理器有复杂依赖(例如需要配置的依赖)时使用。
要利用此功能,在与 DTO 相同的命名空间中创建一个处理器工厂。假设来自创建命令部分的 MyCommand
,处理器工厂看起来像这样
<?php namespace My\Command; use Zumba\CQRS\Command\HandlerFactory; use Zumba\CQRS\Command\Handler; class MyCommandHandlerFactory implements HandlerFactory { public static function make(): Handler { return new MyCommandHandler(); } }
方法提供者
Zumba\CQRS\Provider\MethodProvider
与 ClassProvider
类似,但处理器本身可以作为自己的工厂。
可以从创建命令处理器部分修改 MyCommandHandler
。
<?php namespace My\Command; use Zumba\CQRS\Command\Command; use Zumba\CQRS\Command\CommandResponse; use Zumba\CQRS\Command\Handler; use Zumba\CQRS\Command\HandlerFactory; use Zumba\CQRS\CommandService; final class MyCommandHandler implements Handler, HandlerFactory { public function handle(Command $command, CommandService $commandService): CommandResponse { echo $command->message; return CommandResponse::fromSuccess(); } public static function make(): Handler { return new static(); } }
简单依赖提供者
在所有依赖都可以无参数实例化的情况下,可以使用 Zumba\CQRS\Provider\SimpleDependencyProvider
来构建处理器,无需使用 HandlerFactory
。
创建自定义提供者
要创建自己的自定义提供者,实现 Zumba\CQRS\Provider
接口,并使用自定义提供者创建总线。您还可以提供额外的提供者,以便在自定义提供者无法适应 DTO 的情况下进行回退。
<?php use My\Provider\CustomProvider; $bus = CommandBus::fromProviders( new CustomProvider(), new ClassProvider(), );
中间件
CQRS 库配备有功能,允许将中间件附加到命令或查询总线,以便为任何或所有 DTO 执行通用操作。
日志中间件
日志中间件允许记录通过总线分发的所有 DTO。
<?php use Psr\Log\LogLevel; use Psr\Log\NullLogger; use Zumba\CQRS\CommandBus; use Zumba\CQRS\MiddlewarePipeline; use Zumba\CQRS\Middleware\Logger; $bus = CommandBus::defaultBus(); // Substitute with a real Psr logger. $logger = new NullLogger(); $middlewarePipeline = MiddlewarePipeline::fromMiddleware( Logger::fromLoggerAndLevel( $logger, LogLevel::INFO, ) ); $bus = $bus->withMiddleware($middlewarePipeline);
自定义中间件
只要实现了 Middleware
接口,就可以将自定义中间件包含在 MiddlewarePipeline
中。
handle
方法将接受一个 DTO 和一个用于继续过程的 callable
。
请参见 Logger
中间件作为示例。