star/domain-event

领域事件扩展

2.5.2 2024-01-21 17:59 UTC

This package is auto-updated.

Last update: 2024-09-21 19:35:13 UTC


README

Build Status

ddd中实现聚合根的小型示例。AggregateRoot实现会触发事件,可以被EventPublisher的实现收集并发布。

安装

  • 使用composer在您的composer.json中添加包

composer require star/domain-event

使用

  1. 让您的类继承AggregateRoot类。
// Product.php
class Product extends AggregateRoot
{
}
  1. 为变更创建事件。
class ProductWasCreated implements DomainEvent
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function name(): string
    {
        return $this->name;
    }
}
  1. 创建一个命名构造函数(静态方法)或变更方法。
// Product.php
    /**
     * Static construct, since the __construct() is protected
     *
     * @param string $name
     * @return Product
     */
    public static function draftProduct(string $name): Product
    {
        return self::fromStream([new ProductWasCreated($name)]);
    }
    
    /**
     * Mutation method that handles the business logic of your aggregate
     */
    public function confirm(): void
    {
        $this->mutate(new ProductWasConfirmed($this->getId()));
    }
  1. 在AggregateRoot上创建一个回调方法,该方法用于在事件变更后设置状态。
    protected function onProductWasCreated(ProductWasCreated $event): void
    {
        $this->name = $event->name();
    }
}

监听事件

当您希望在EventPublisher发布事件后执行操作时,您需要定义您的监听器

class DoSomethingProductCreated implements EventListener
{
    // methods on listener can be anything, it is configured by listensTo
    public function doSomething(ProductWasCreated $event): void
    {
        // do something with the event
    }

    public function doSomethingAtFirst(PostWasPublished $event): void 
    {
    }

    public function doSomethingInBetween(PostWasPublished $event): void 
    {
    }

    public function doSomethingAtLast(PostWasPublished $event): void 
    {
    }
    
    public function listensTo(): array
    {
        return [
            ProductWasCreated::class => 'doSomething', // priority will be assigned at runtime
            PostWasPublished::class => [ // multiple methods may be assigned priority
                100 => 'doSomethingAtFirst',
                0 => 'doSomethingInBetween',
                -20 => 'doSomethingAtLast',
            ],
        ];
    }
}

监听器需要提供给发布者,以便它可以发送事件。

$publisher = new class() implements EventPublisher {}; // your implementation choice
$publisher->subscribe(new DoSomethingProductCreated()); // This is a subscriber that listens to the ProductWasCreated event

$product = Product::draftProduct('lightsaber');
$publisher->publishChanges($product->uncommitedEvents()); // will notify the listener and call the DoSomethingProductCreated::doSomething() method

警告:请注意,一旦收集,事件将从聚合中删除,以避免重复发布同一事件。

我们目前支持第三方适配器,允许您将库集成到您的基础设施中。

命名规范

AggregateRoot子类的events方法必须以前缀on开头,后跟事件名。例如,对于名为StuffWasDone的事件类,聚合应该有一个方法

protected function onStuffWasDone(StuffWasDone $event): void;

注意:可以通过覆盖AggregateRoot::getEventMethod()将回调方法更改为其他格式。

消息总线

该包添加了派发消息(命令和查询)的能力。与EventPubliser相比,CommandBus和QueryBus有不同的用法。

  • 命令总线:负责派发一个不返回任何内容的操作。
  • 查询总线:负责获取一些信息。建议将返回的信息以只读格式返回。

(使用示例)

示例

blog示例展示了博客应用程序的使用场景。

Symfony使用

使用Symfony应用程序,您可以使用提供的编译器传递来使用总线。

use Star\Component\DomainEvent\Ports\Symfony\DependencyInjection\CommandBusPass;
use Star\Component\DomainEvent\Ports\Symfony\DependencyInjection\EventPublisherPass;
use Star\Component\DomainEvent\Ports\Symfony\DependencyInjection\QueryBusPass;

// Kernel.php
public function build(ContainerBuilder $container): void {
    $container->addCompilerPass(new CommandBusPass());
    $container->addCompilerPass(new QueryBusPass());
    $container->addCompilerPass(new EventPublisherPass());
}

一旦注册,将提供三个新标签

  • star.command_handler
  • star.query_handler
  • star.event_publisher

标签star.command_handlerstar.query_handler有一个可选属性message,用于指定映射到该处理器的消息FQCN。默认情况下,系统将尝试解析与处理器相同的FQCN,但无需Handler后缀。

// services.yaml
services:
    Path/For/My/Project/DoStuffHandler:
      tags:
        - { name star.command_handler, message: Path/For/My/Project/DoStuff }

    Path/For/My/Project/FetchStuffHandler:
      tags:
        - { name star.query_handler, message: Path\For\My\Project\FetchStuff }

注意:在这两种情况下,省略message属性将导致相同的行为。

事件存储

事件存储是您事件持久化的地方。您通过扩展提供的存储来定义使用哪个平台。

示例DBALEventStore