somnambulist/domain-events

此包已被废弃,不再维护。作者建议使用somnambulist/domain包。

为您的实体提供域事件,可用于Doctrine。

1.1.1 2018-06-05 12:55 UTC

This package is auto-updated.

Last update: 2020-02-03 17:23:51 UTC


README

此存储库已被存档。请更新到组合包。

PHP 实体域事件

为普通PHP实体添加对域事件的支持。此包受Benjamin Eberlei的Gist和博客文章的启发

要求

  • PHP 7+
  • bcmath 扩展

安装

使用composer安装,或从github.com检出/拉取文件。

  • composer install somnambulist/domain-events
  • 对于Doctrine,将Doctrine\DomainEventPublisher注册到您的EntityManagers
  • 对于Symfony,注册Symfony\DomainEventPublisher
  • 对于Laravel,注册Laravel\DomainEventPublisher
  • 实现可以引发事件并刷新/派发它们的实体/对象
  • 实现您自己的事件发布者

引发事件

为了引发事件,决定哪些操作应该导致域事件。这些应该与域对象的状态变化相一致,并且事件应该从您的主体实体(聚合根)中产生。

例如:您可能希望在创建新的User实体或为用户添加角色时引发事件。

这确实需要对您通常与实体和Doctrine一起工作的方式做出一些改变,即您应该删除setter和可以为空的构造函数参数。相反,您需要通过特定方法来管理实体中的更改,例如

  • completeOrder()
  • updatePermissions()
  • revokePermissions()
  • publishStory()

在内部,更新实体状态后,调用:$this->raise(new NameOfEvent([])) 并将任何您希望传递给监听器的特定参数传递到事件中。这可能包括旧值和新值或整个实体引用,完全取决于您。

public function __construct($id, $name, $another, $createdAt)
{
    $this->id        = $id;
    $this->name      = $name;
    $this->another   = $another;
    $this->createdAt = $createdAt;
    $this->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another]));
}

通常最好不在构造函数中引发事件,而是使用命名构造函数来创建主要对象

private function __construct($id, $name, $another, $createdAt)
{
    $this->id        = $id;
    $this->name      = $name;
    $this->another   = $another;
    $this->createdAt = $createdAt;
    $this->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another]));
}

public static function create($id, $name, $another)
{
    $entity = new static($id, $name, $another, new DateTime());
    $entity->raise(new MyEntityCreatedEvent(['id' => $id, 'name' => $name, 'another' => $another]));
    
    return $entity;
}

触发域事件

根据您的框架/库选择,提供了一些集成。

Doctrine集成

此实现包括一个Doctrine订阅者,它将监听实现RaisesDomainEvent接口的实体,并确保调用releaseAndResetEvents()

  • 注意:不需要使用DomainEventPublisher订阅者。您可以实现自己的事件分发器,使用完全不同的分发器(框架)并手动触发域事件,通过刷新更改,然后手动调用releaseAndResetEvents并分发事件。

    如果您这样做,请注意,聚合根类和主标识符(如果使用)将不会自动设置。如果您打算使用它们,您需要更新代码来设置这些。

要使用包含的监听器,请将其添加到Doctrine配置中的事件订阅者列表中。这是针对每个实体管理器进行的。

  • 注意:要使用依赖于Doctrine仓库的领域事件监听器,必须将加载这些订阅者推迟到Doctrine解决之后。

领域事件实例通过一个与Doctrine EventArgs 兼容的层进行代理,该层保留了原始事件和名称。

Symfony集成

包含了一个基本的Symfony EventDispatcher桥接器。您可以使用它与Symfony框架注册触发事件的对象,然后通过调用Symfony\DomainEventPublisher::dispatch()在任何时候触发发布。所有注册的实体将收集、排序并通过主要的Symfony EventDispatcher分发领域事件。

领域事件通过一个与Symfony兼容的事件代理进行代理,该代理将事件名称自动转换为Symfony点名称(entity.created)。这保留了原始事件、名称和属性。

要监听事件,创建一个监听器并将其注册到主事件分发器,以匹配点表示法的事件。

Laravel集成

Laravel的事件分发器没有类型化事件,因此您不需要对事件进行任何特殊转换。提供了一个集成来注册类似于Symfony集成的触发事件的对象。

class SomeServiceClass
{
    protected $publisher;
    
    public function __construct($publisher)
    {
        $this->publisher = $publisher;
    }

    public function doSomethingComplicated()
    {
        $object = new SomeDomainObject(); // or fetch from wherever
        $this->publisher->publishEventsFrom($object);
        
        $object->callSomeMethod();
        $object->callSomeOtherMethod();
        $object->doSomethingElse();
        
        $this->publisher->dispatch();
    }
}

这将按顺序通过标准的Laravel事件分发器分发事件。

要监听事件,以通常方式在Laravel项目中绑定您的监听器:请确保使用Event::class

Symfony / Laravel自动分发

如果使用独立发布者,可以创建一个onTerminate中间件/内核监听器,在请求结束时触发所有领域事件。

请记住,您需要事先注册将触发事件的对象。

创建领域事件监听器

监听器可以有自己的依赖关系(构造函数未定义),并在onFlush工作单元事件之后被调用。监听器可以执行必要的任何后处理操作,甚至触发更多事件。

监听器应该添加以下命名的

  • onNameOfTheEvent
  • 方法(不带“event”后缀)
  • 该方法将接收领域事件实例
  • 领域事件将具有聚合的类和id

单元测试中的示例

class EventListener
{    
    public function onMyEntityCreated(MyEntityCreatedEvent $event)
    {
        printf(
            "New item created with id: %s, name: %s, another: %s",
            $event->getProperty('id'),
            $event->getProperty('name'),
            $event->getProperty('another')
        );
    }
}

单元测试显示了如何实现。

务必阅读之前提到的Benjamin Eberlei的文章,并查看他的断言库,以实现低依赖实体验证。