cvek/domain-events

添加领域事件功能的组件

安装: 381

依赖项: 0

建议者: 0

安全: 0

星级: 2

关注者: 1

分支: 1

开放性问题: 0

类型:symfony-bundle

3.0.0 2022-05-12 12:01 UTC

README

此包集成了Symfony应用程序以提供领域事件支持。这允许在业务逻辑实现期间无Doctrine依赖地触发事件。

领域事件分为三种类型

  • preFlush。在持久化到数据库之前将同步处理;
  • onFlush。在持久化到数据库期间(同步或异步 - 见下文)处理;
  • postFlush。在持久化到数据库之后(同步或异步 - 见下文)处理。

要使用此包,请在您的实体类中实现 RaiseEventsInterface 接口并创建自定义领域事件。我们建议您使用 RaiseEventsTrait 来简化此过程。

同步/异步消息

任何领域事件都可以在 onFlushpostFlush Doctrine事件期间以 syncasync 方式执行。通过扩展以下抽象类之一: AbstractSyncDomainEventAbstractAsyncDomainEvent 来获得相应的 syncasync 事件。

异步方式是一种非常强大的方法,必须在以下情况下使用

  • 您想推迟一些耗时的或远程任务;
  • 您想避免Doctrine事件系统的限制(例如,见此处此处)。

多亏了 db 传输与doctrine存储(将自动创建) - 所有异步领域事件都将自动路由并持久化到数据库。

为了消费它们,我们建议使用以下supervisor配置

[program:db]
command=/srv/api/bin/console messenger:consume db --memory-limit=128M --time-limit=3600 --limit=100
process_name=%(program_name)s_%(process_num)02d
numprocs=1
autostart=true
autorestart=true
startsecs=0
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

⚠️ 不要忘记将其添加到您的应用程序中!

直接异步消息

如果您不想编写自己的异步消息处理程序,但直接派发消息,则只需在您的消息中扩展 AbstractDirectAsyncDomainEvent 并以此方式使用它即可

use Doctrine\ORM\Events;

public function setName(string $name): self
{
    $this->name = $name;
    $this->raise(new NameChangedDirectAsyncMessage($this));

    return $this;
}

消息将被直接派发到总线,而无需编写自己的处理程序。

示例

安装

composer req cvek/domain-events

使用

创建领域事件

同步事件
use \Cvek\DomainEventsBundle\EventDispatch\Event\AbstractSyncDomainEvent;

final class FooNameChanged extends AbstractSyncDomainEvent
{
    private Foo $foo;
    private string $oldName;
    private string $newName;

    public function __construct(Foo $foo, string $oldName, string $newName)
    {
        $this->foo = $foo;
        $this->oldName = $oldName;
        $this->newName = $newName;
    }
   
    public function getFoo(): Foo
    {
        return $this->foo;
    }
    
    public function getOldName(): string
    {
        return $this->oldName;
    }
    
    public function getNewName(): string
    {
        return $this->newName;
    }

    public function isAsync() : bool
    {
        return false;
    }
}
异步事件
use \Cvek\DomainEventsBundle\EventDispatch\Event\AbstractAsyncDomainEvent;

final class FooPasswordChanged extends AbstractAsyncDomainEvent
{
    private Foo $foo;
    private string $password;

    public function __construct(Foo $foo, string $password)
    {
        $this->foo = $foo;
        $this->password = $password;
    }
   
    public function getFoo(): Foo
    {
        return $this->foo;
    }
    
    public function getPassword(): string
    {
        return $this->password;
    }

    public function isAsync() : bool
    {
        return true;
    }
}

在业务层中触发事件

use \Cvek\DomainEventsBundle\Entity\RaiseEventsInterface;
use \Cvek\DomainEventsBundle\Entity\RaiseEventsTrait;

class Foo implements RaiseEventsInterface
{
    use RaiseEventsTrait;

    private string $name;

    public function setName(string $name): self
    {
        $this->raise(new FooNameChanged($this, $this->name, $name));
        $this->name = $name;

        return $this;
    }

    public function setPassword(string $password): self
    {
        $this->raise(new FooPasswordChanged($this, $password));

        return $this;
    }
}

在监听器中捕获事件

当调用 flush 操作时,所有在您的实体中引发的事件将被收集并派发。您可以通过常规方式监听它们。

监听同步消息
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
use \Doctrine\ORM\Events;

final class FooNameListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            FooNameChanged::class => 'onNameChange'
        ];
    }

    public function onNameChange(FooNameChanged $event): void
    {
        if ($event->getLifecycleEvent() === Events::preFlush) {
            // your custom logic on preFlush moment: logging, validation etc...        
        }

        if ($event->getLifecycleEvent() === Events::onFlush) {
            // your custom logic on onFlush moment        
        }

        if ($event->getLifecycleEvent() === Events::postFlush) {
            // your custom logic on postFlush moment        
        }
    }
}
监听异步消息
use \Doctrine\ORM\Events;
use \Symfony\Component\Messenger\Handler\MessageHandlerInterface;

final class FooPasswordHandler implements MessageHandlerInterface
{
    public function __invoke(FooPasswordChanged $event)
    {
        if ($event->getLifecycleEvent() === Events::preFlush) {
            // your custom logic on preFlush moment: logging, validation etc...        
        }

        if ($event->getLifecycleEvent() === Events::onFlush) {
            // your custom logic on onFlush moment        
        }

        if ($event->getLifecycleEvent() === Events::postFlush) {
            // your custom logic on postFlush moment        
        }
    }
}