domain-events-bundle:Symfony的DDD域事件

  • N9_423074
    了解作者
  • 29.7KB
    文件大小
  • zip
    文件格式
  • 0
    收藏次数
  • VIP专享
    资源类型
  • 0
    下载次数
  • 2022-05-27 14:06
    上传日期
域事件包 Symfony的DDD域事件,具有基于Doctrine的事件存储。 使用此程序包,您可以从域模型内调度域事件,以便将它们与聚合持久化在同一事务中。 然后,这些事件在使用Symfony的事件侦听器发布kernel.TERMINATE事件。 这样可以确保交易的一致性,并确保通过发件箱模式进行交付。 需要Symfony 4.4或Symfony 5.x 安装 composer require headsnet/domain-events-bundle (有关先决条件,请参见下面的) 域事件类 域事件类必须使用聚合的根ID实例化。 您可以根据需要向构造函数添加其他参数。 use Headsnet \ DomainEventsBundle \ Domain \ Model \ DomainEvent ; use Headsnet \ DomainEventsBundle \
domain-events-bundle-master.zip
内容介绍
# Domain Event Bundle [![Build Status](https://travis-ci.com/headsnet/domain-events-bundle.svg?branch=master)](https://travis-ci.com/headsnet/domain-events-bundle) [![Latest Stable Version](https://poser.pugx.org/headsnet/domain-events-bundle/v)](//packagist.org/packages/headsnet/domain-events-bundle) [![Total Downloads](https://poser.pugx.org/headsnet/domain-events-bundle/downloads)](//packagist.org/packages/headsnet/domain-events-bundle) [![License](https://poser.pugx.org/headsnet/domain-events-bundle/license)](//packagist.org/packages/headsnet/domain-events-bundle) DDD Domain Events for Symfony, with a Doctrine based event store. This package allows you to dispatch domain events from within your domain model, so that they are persisted in the same transaction as your aggregate. These events are then published using a Symfony event listener in the `kernel.TERMINATE` event. This ensures transactional consistency and guaranteed delivery via the Outbox pattern. _Requires Symfony 4.4, or Symfony 5.x_ ### Installation ```bash composer require headsnet/domain-events-bundle ``` (see [Messenger Component](#messenger-component) below for prerequisites) ### The Domain Event Class A domain event class must be instantiated with an aggregate root ID. You can add other parameters to the constructor as required. ```php use Headsnet\DomainEventsBundle\Domain\Model\DomainEvent; use Headsnet\DomainEventsBundle\Domain\Model\Traits\DomainEventTrait; final class DiscountWasApplied implements DomainEvent { use DomainEventTrait; public function __construct(string $aggregateRootId) { $this->aggregateRootId = $aggregateRootId; $this->occurredOn = (new \DateTimeImmutable)->format(DateTime::ATOM); } } ``` ### Recording Events Domain events should be dispatched from within your domain model - i.e. from directly inside your entities. Here we record a domain event for entity creation. It is then automatically persisted to the Doctrine `event` database table in the same database transaction as the main entity is persisted. ```php use Headsnet\DomainEventsBundle\Domain\Model\ContainsEvents; use Headsnet\DomainEventsBundle\Domain\Model\RecordsEvents; use Headsnet\DomainEventsBundle\Domain\Model\Traits\EventRecorderTrait; class MyEntity implements ContainsEvents, RecordsEvents { use EventRecorderTrait; public function __construct(PropertyId $uuid) { $this->uuid = $uuid; // Record a domain event $this->record( new DiscountWasApplied($uuid->asString()) ); } } ``` Then, in `kernel.TERMINATE` event, a listener automatically publishes the domain event on to the `messenger.bus.event` event bus for consumption elsewhere. ### Amending domain events Even though events should be treated as immutable, it might be convenient to add or change meta data before adding them to the event store. Before a domain event is appended to the event store, the standard Doctrine event store emits a `PreAppendEvent` Symfony event, which can be used e.g. to set the actor ID as in the following example: ```php use App\Entity\User; use Headsnet\DomainEventsBundle\Doctrine\Event\PreAppendEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Security; final class AssignDomainEventUser implements EventSubscriberInterface { private Security $security; public function __construct(Security $security) { $this->security = $security; } public static function getSubscribedEvents(): array { return [ PreAppendEvent::class => 'onPreAppend' ]; } public function onPreAppend(PreAppendEvent $event): void { $domainEvent = $event->getDomainEvent(); if (null === $domainEvent->getActorId()) { $user = $this->security->getUser(); if ($user instanceof User) { $domainEvent->setActorId($user->getId()); } } } } ``` ### Deferring Events Into The Future If you specify a future date for the `DomainEvent::occurredOn` the event will not be published until this date. This allows scheduling of tasks directly from within the domain model. #### Replaceable Future Events If an event implements `ReplaceableDomainEvent` instead of `DomainEvent`, recording multiple instances of the same event for the same aggregate root will overwrite previous recordings of the event, as long as it is not yet published. For example, say you have an aggregate _Booking_, which has a future _ReminderDue_ event. If the booking is then modified to have a different date/time, the reminder must also be modified. By implementing `ReplaceableDomainEvent`, you can simply record a new _ReminderDue_ event, and providing that the previous _ReminderDue_ event had not been published, it will be removed and superseded by the new _ReminderDue_ event. ### Event dispatching By default only the DomainEvent is dispatched to the configured event bus. You can overwrite the default event dispatcher with your own implementation to annotate the message before dispatching it, e.g. to add an envelope with custom stamps. Example: ```yaml services: headsnet_domain_events.domain_event_dispatcher_service: class: App\Infrastructure\DomainEventDispatcher ``` ```php class PersonCreated implements DomainEvent, AuditableEvent { … } ``` ```php class DomainEventDispatcher implements \Headsnet\DomainEventsBundle\EventSubscriber\DomainEventDispatcher { private MessageBusInterface $eventBus; public function __construct(MessageBusInterface $eventBus) { $this->eventBus = $eventBus; } public function dispatch(DomainEvent $event): void { if ($event instanceof AuditableEvent) { $this->eventBus->dispatch( new Envelope($event, [new AuditStamp()]) ); } else { $this->eventBus->dispatch($event); } } } ``` ### Messenger Component By default, the bundle expects a message bus called `messenger.bus.event` to be available. This can be configured using the bundle configuration - see [Default Configuration](#default-configuration). ```yaml framework: messenger: … buses: messenger.bus.event: # Optional default_middleware: allow_no_handlers ``` [Symfony Messenger/Multiple Buses](https://symfony.com/doc/current/messenger/multiple_buses.html) ### Doctrine The bundle will create a database table called `event` to persist the events in before dispatch. This allows a permanent record of all events raised. The `StoredEvent` entity also tracks whether each event has been published to the bus or not. Finally, a Doctrine DBAL custom type called `datetime_immutable_microseconds` is automatically registered. This allows the StoredEvent entity to persist events with microsecond accuracy. This ensures that events are published in the exact same order they are recorded. ### Default Configuration ```yaml headsnet_domain_events: message_bus: name: messenger.bus.event ``` ### Contributing Contributions are welcome. Please submit pull requests with one fix/feature per pull request. Composer scripts are configured for your convenience: ``` > composer test # Run test suite > composer cs # Run coding standards checks > composer cs-fix # Fix coding standards violations > composer static # Run static analysis with Phpstan ```
评论
    相关推荐
    • micro-symfony
      存储库中包含截屏代码,脚本以及您可以从KnpUniversity的WIP“ Micro-Symfony Framework”课程中学习的所有AJAX。 订阅更新: 合作 当我们开始为本教程编写内容时,我们邀请您通读它,尝试尝试并提供改进,无论是...
    • symfony-skeleton-vkernel:Symfony 4+的多应用程序框架
      具有虚拟内核的Symfony 5骨架,可用于多个应用程序 基于名称的虚拟内核 术语“虚拟内核”是指在单个项目存储库上运行多个应用程序(例如api.example.com和admin.example.com )的实践。 虚拟内核是“基于名称的”,...
    • TicketingBundle:票务捆绑Symfony 4
      票务包 TicketingBundle是一个社区驱动的项目,由来自的一群学生创建和维护。 安装 将TicketingBundle添加到您的项目 $ composer require maps_red/ticketing-bundle 文献资料 ... symfony社区的帮助
    • cli:报告与Symfony CLI相关的问题的存储
      cli:报告与Symfony CLI相关的问题的存储
    • userapp-symfony:将 Userapp.io 服务集成为 Symfony 的用户提供者
      用户信息(基本细节、权限和属性)在登录时存储Symfony 用户会话中,以避免在每次请求时调用 API。 这意味着只有当用户重新登录时,这些数据才会在 Symfony 应用程序中刷新。 对于每个请求,都可以向 API 发送...
    • CommanderBundle:Symfony2 CommandBus 实现
      大部分文档存储在此包的Resources/doc/index.md文件中: 安装 所有安装说明都位于文档中。 执照 此捆绑包在 MIT 许可下。 查看捆绑包中的完整许可证: Resources/meta/LICENSE 关于 CommanderBundle 是 Tabbi89 的...
    • symfony-hackday:Symfony Hackday 存储
      symfony 黑客日 这是一个包含在 Symfony 11 月 30 日 hackday 中使用的项目的存储库。 运行应用程序 ./run.sh [port]
    • PasswordlessBundle:Symfony 无密码实现
      Symfony 2 无密码包 ... 这是试图将密码存储消除到尽可能少的地方。 厌倦了密码生成器? 密码保险箱? 密码被黑客入侵? 记住和忘记密码? 或者你只是想为你的用户提供一些输入最少的东西? 流程是
    • symfony_trips
      Trips Symfony Trips Symfony是使用PHP和Symfony开发的平台,用于管理有关旅行和活动的特定域,并存储与活动位置和地点有关的所有信息(如酒店,餐厅,俱乐部,古迹)。
    • symfony-storage
      Symfony存储 这是一个使用Symfony的翻译编写器和加载器PHP翻译存储适配器。 安装 composer require php-translation/symfony-storage 测验 通过运行以下命令进行静态代码分析(修复代码样式,并使用PHPStan进行测试...