smartgecko/governor-framework

PHP 命令-查询-职责分离框架。

0.3.0 2014-12-18 14:20 UTC

This package is not auto-updated.

Last update: 2024-09-14 16:55:37 UTC


README

Build Status Scrutinizer Code Quality Code Coverage

Governor Framework 是一个适用于 PHP 5.5+ 的命令和查询职责分离库。

它提供组件来构建遵循 CQRS 模式的应用程序,例如

  • 命令处理
  • 事件处理
  • 事件溯源
  • 事件存储
  • Sagas
  • 工作单元
  • 存储库
  • 序列化
  • AMQP 支持
  • 测试

这个库的主要灵感来源是 Java 编写的 Axon 框架,而 Governor 框架可以看作是 Axon 的 PHP 版本,因为它保留了其基本构建块。

这个库可以直接集成到 Symfony 2 框架中作为一个包。Symfony 2 集成的核心来自 LiteCQRS 框架

快速入门

领域模型

为了展示 Governor 框架的功能,我们将使用一个相当简单的用户模型。用户类是我们的聚合根。在 Governor 中,聚合根必须实现 Governor\Framework\Domain\AggregateRootInterface 接口或扩展内置的基类之一

  • Governor\Framework\Domain\AbstractAggregateRoot
  • Governor\Framework\EventSourcing\AbstractEventSourcedAggregateRoot
  • Governor\Framework\EventSourcing\Annotation\AbstractAnnotatedAggregateRoot

在这个例子中,我们将使用具有注解支持的聚合根。在我们的聚合根中,还将作为示例中的命令处理器。

聚合根将响应以下命令

  • CreateUserCommand
  • ChangeUserEmailCommand

这将产生相应的事件

  • UserCreatedEvent
  • UserEmailChangedEvent

我们的聚合根实现将如下所示。

class User extends AbstractAnnotatedAggregateRoot 
{  
    /**
     * @AggregateIdentifier
     * @var string
     */
    private $identifier;
    private $email;
    
    /**
     * @CommandHandler
     * @param CreateUserCommand $command
     */
    public function __construct(CreateUserCommand $command)
    {
        $this->apply(new UserCreatedEvent($command->getIdentifier(), $command->getEmail()));
    }

    /**
     * @CommandHandler
     * @param ChangeUserEmailCommand $command
     */
    public function changeEmail(ChangeUserEmailCommand $command)
    {
        $this->apply(new UserEmailChangedEvent($this->identifier, $command->getEmail()));
    }

    /**
     * @EventHandler
     * @param UserEmailChangedEvent $event
     */
    public function onEmailChanged(UserEmailChangedEvent $event)
    {
        $this->email = $event->getEmail();
    }

    /**
     * @EventHandler
     * @param UserCreatedEvent $event
     */
    public function onUserCreated(UserCreatedEvent $event)
    {
        $this->identifier = $event->getIdentifier();
        $this->email = $event->getEmail();
    }

    public function getEmail()
    {
        return $this->email;
    }

}

命令和事件

我们在聚合根上引入了两个操作 - 一个将创建新用户,另一个将更改用户电子邮件。请注意事件类上的 @Type 注解 - Governor 框架使用出色的 JMS 序列化库用于序列化和反序列化。为了在从事件流重新构建聚合根时允许事件反序列化,必须存在注解才能成功反序列化事件。

我们的命令和事件实现如下

abstract class AbstractUserEvent
{

    /**
     * @Type("string")
     * @var string
     */
    private $identifier;

    /**
     * @Type("string")
     * @var string
     */
    private $email;

    function __construct($identifier, $email)
    {
        $this->identifier = $identifier;
        $this->email = $email;
    }

    public function getIdentifier()
    {
        return $this->identifier;
    }

    public function getEmail()
    {
        return $this->email;
    }

}

class UserCreatedEvent extends AbstractUserEvent
{
    
}

class UserEmailChangedEvent extends AbstractUserEvent
{
    
}

abstract class AbstractUserCommand
{
    /**
     * @TargetAggregateIdentifier
     * @var string 
     */
    private $identifier;
    private $email;

    function __construct($identifier, $email)
    {
        $this->identifier = $identifier;
        $this->email = $email;
    }

    public function getIdentifier()
    {
        return $this->identifier;
    }

    public function getEmail()
    {
        return $this->email;
    }

}

class CreateUserCommand extends AbstractUserCommand
{
    
}

class ChangeUserEmailCommand extends AbstractUserCommand
{
    
}

事件监听器

我们可以注册事件监听器来监听事件总线上的事件。事件监听器需要实现 Governor\Framework\EventHandling\EventListenerInterface 接口。

这是一个简单的监听器,它将监听事件总线上的所有事件并打印它们的负载。

class UserEventListener implements EventListenerInterface
{

    public function handle(EventMessageInterface $event)
    {
        print_r($event->getPayload());
    }

}

整合一切

现在,我们可以通过以下步骤设置必要的基础设施来完成示例

  1. 我们需要设置一个 PSR-0 兼容的日志记录器,如 Monolog。
  2. 创建一个命令总线,并为方便将其包装在一个命令网关中。
  3. 初始化一个基于文件系统的事件存储。请注意,如果您不想使用事件溯源,则可以跳过此步骤。
  4. 设置一个简单的事件总线
  5. 创建一个事件溯源存储库
  6. 将注解聚合根订阅到命令总线,以便它可以接收命令。
  7. 注册一个将显示事件内容的监听器。
  8. 分发命令。
// set up logging 
$logger = new Logger('governor');
$logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));

// 1. create a command bus and command gateway
$commandBus = new SimpleCommandBus();
$commandBus->setLogger($logger);
$commandGateway = new DefaultCommandGateway($commandBus);

$rootDirectory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'CommandHandlerExample';
@mkdir($rootDirectory);
echo sprintf("Initializing FileSystemEventStore in %s\n", $rootDirectory);

// 2. initialize the event store
$eventStore = new FilesystemEventStore(new SimpleEventFileResolver($rootDirectory),
    new JMSSerializer());

// 3. create the event bus
$eventBus = new SimpleEventBus();
$eventBus->setLogger($logger);

// 4. create an event sourcing repository
$repository = new EventSourcingRepository(User::class,
    $eventBus, new NullLockManager(), $eventStore,
    new GenericAggregateFactory(User::class));

//5. create and register our commands
AnnotatedAggregateCommandHandler::subscribe(User::class, $repository, $commandBus);

//6. create and register the eventlistener
$eventListener = new UserEventListener();
$eventBus->subscribe($eventListener);

//7. send commands 
$aggregateIdentifier = Uuid::uuid1()->toString();

$commandGateway->send(new CreateUserCommand($aggregateIdentifier,
    'email@davidkalosi.com'));
$commandGateway->send(new ChangeUserEmailCommand($aggregateIdentifier,
    'newemail@davidkalosi.com'));

许可

Governor 框架采用 MIT 许可证。