zumba/cqrs

CQRS 库

v2.0.0 2023-11-14 14:47 UTC

This package is auto-updated.

Last update: 2024-09-14 16:29:33 UTC


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\MethodProviderClassProvider 类似,但处理器本身可以作为自己的工厂。

可以从创建命令处理器部分修改 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 中间件作为示例。