itantik / cq-dispatcher
支持中间件的命令查询调度器。
Requires
- php: >= 7.2
- itantik/middleware: ^0.2
Requires (Dev)
- nette/bootstrap: ^3.0
- nette/di: ^v3.0
- nette/tester: ^2.0
- phpstan/extension-installer: ^1.0
- phpstan/phpstan: ^0.12.19
- phpstan/phpstan-nette: ^0.12.6
- squizlabs/php_codesniffer: 3.5.5
This package is auto-updated.
Last update: 2024-09-09 01:09:36 UTC
README
支持中间件的命令查询调度器。该库可以应用于根据 命令查询分离 (CQS) 或 命令查询责任分离 (CQRS) 原则设计的应用程序。
CQ Dispatcher 通常位于 应用服务层,其客户端为来自 UI 层 的控制器和展示者。它调度应用请求,这些请求是命令和查询,找到合适的处理器并执行它。
命令 表示一个通过命令处理器执行操作的请求。它 修改 数据或改变对象的状态。它 不返回 值。
查询 表示一个通过查询处理器获取数据的请求。它 不得修改 数据。它 返回 一个值。
安装
composer require itantik/cq-dispatcher
用法
CQ Dispatcher 需要一个依赖注入容器。您必须定义一个适配器到您的 DI 容器。适配器实现 Itantik\CQDispatcher\DI\IContainer
。
与 Nette 框架一起使用
安装 itantik/nette-cq-dispatcher 扩展,用于 Nette 框架。它已配置为使用 Nette DI 容器。
文件结构示例
假设应用服务层具有以下文件结构
- UserService
- UserCommands.php // command dispatcher
- UserQueries.php // query dispatcher
- Command
- AddUserCommand.php
- AddUserHandler.php
- ChangePasswordCommand.php
- ChangePasswordHandler.php
- ... // other commands/handlers
- Query
- FindAllUsersQuery.php
- FindAllUsersHandler.php
- GetUserQuery.php
- GetUserHandler.php
- ... // other queries/handlers
- Middleware
- TransactionalMiddleware.php
命令
命令是一个实现了 Itantik\Middleware\IRequest
接口的普通对象,其类名使用可选的 Command
后缀。命令对象代表您的请求。
class AddUserCommand implements IRequest { /** @var int */ private $id; /** @var string */ private $name; /** @var string */ private $surname; public function __construct(int $id, string $name, string $surname) { $this->id = $id; $this->name = $name; $this->surname = $surname; } public function id(): int { return $this->id; } public function name(): string { return $this->name; } public function surname(): string { return $this->surname; } }
命令处理器
命令处理器实现了 Itantik\CQDispatcher\Command\ICommandHandler
接口,类名应使用 Handler
后缀。命令处理器代表一个应用服务。
class AddUserHandler implements ICommandHandler { /** @var UserRepository */ private $repository; public function __construct(UserRepository $repository) { $this->repository = $repository; } public function handle(AddUserCommand $command): void { $user = new User($command->id(), $command->name(), $command->surname()); $this->repository->add($user); } }
命令调度器
命令调度器根据命令类名创建适当的命令处理器,然后调用其 handle
方法。对于每个命令,都有一个单独的命令处理器。
默认情况下,命令与其处理器的配对基于类名。
命令 Namespace\SomeCommand
使用 Namespace\SomeHandler
处理器。 Command
后缀是可选的,因此您可以命名命令为 Namespace\Some
而不是 Namespace\SomeCommand
。
创建命令调度器
命令调度器扩展了抽象类 \Itantik\CQDispatcher\Commands
。
class UserCommands extends \Itantik\CQDispatcher\Commands { }
在控制器和展示者中使用
// simplified code class UserController { /** @var UserCommands */ private $userCommands; public function actionAddUser(string $name, string $surname): void { // create a command $id = SomeIdGenerator::next(); $command = new AddUserCommand($id, $name, $surname); try { // execute command $this->userCommands->execute($command); } catch (\Exception $ex) { // handle error // ... } // redirect to user detail page // ... } }
扩展中间件
命令调度器内置了对 itantik/middleware 的支持。
例如,将数据库事务包装在每个命令处理器周围的中间件可能看起来像这样
final class TransactionalMiddleware implements Itantik\Middleware\IMiddleware { /** @var DatabaseConnection */ private $connection; public function __construct(DatabaseConnection $connection) { $this->connection = $connection; } public function handle(IRequest $request, ILayer $nextLayer): IResponse { $connection = $this->connection; $connection->beginTransaction(); try { $res = $nextLayer->handle($request); $connection->commit(); return $res; } catch (Exception $e) { $connection->rollback(); throw $e; } } }
扩展命令调度器
class UserCommands extends \Itantik\CQDispatcher\Commands { public function __construct( ICommandDispatcher $commandDispatcher, DatabaseConnection $connection ) { parent::__construct($commandDispatcher); $this->appendMiddleware(new TransactionalMiddleware($connection)); } }
在控制器中使用没有改变。现在每个命令都在数据库事务中执行。
查询
查询实现了 Itantik\Middleware\IRequest
接口,其类名使用可选的 Query
后缀。
class FindAllUsersQuery implements IRequest { }
查询处理器
查询处理器实现了 Itantik\CQDispatcher\Query\IQueryHandler
接口,类名应使用 Handler
后缀,handle
方法返回一个值。
class FindAllUsersHandler implements IQueryHandler { /** @var UserRepository */ private $repository; public function __construct(UserRepository $repository) { $this->repository = $repository; } public function handle(FindAllUsersQuery $query): UserList { return $this->repository->findAll(); } }
查询调度器
类似于命令调度器。
查询 Namespace\SomeQuery
使用 Namespace\SomeHandler
处理器。 Query
后缀是可选的,因此您可以命名查询为 Namespace\Some
而不是 Namespace\SomeQuery
。
创建查询调度器
查询调度器扩展了抽象类 \Itantik\CQDispatcher\Queries
。
class UserQueries extends \Itantik\CQDispatcher\Queries { }
在控制器和展示者中使用
// simplified code class UserController { /** @var UserQueries */ private $userQueries; public function actionAllUsers(): void { // create a query $query = new FindAllUsersQuery(); // execute query $userList = $this->userQueries->execute($query); // fill-in template // ... } }
扩展中间件
与命令调度器相同。
需求
- PHP 7.2