- Notifications
You must be signed in to change notification settings - Fork2
Library to create the domain layer of your DDD application
License
gpslab/domain-event-bundle
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Bundle to create the domain layer of yourDomain-driven design (DDD) application.
ThisSymfony bundle is a wrapper forgpslab/domain-event, look it for more details.
Pretty simple withComposer, run:
composer req gpslab/domain-event-bundle
Example configuration
gpslab_domain_event:# Event bus service# Support 'listener_located', 'queue' or a custom service# As a default used 'listener_located'bus:'listener_located'# Event queue service# Support 'pull_memory', 'subscribe_executing' or a custom service# As a default used 'pull_memory'queue:'pull_memory'# Event listener locator# Support 'symfony', 'container', 'direct_binding' or custom service# As a default used 'symfony'locator:'symfony'# Publish domain events post a Doctrine flush event# As a default used 'false'publish_on_flush:true
Create a domain event
useGpsLab\Domain\Event\Eventclass PurchaseOrderCreatedEventimplements Event{ private$customer_id;private$create_at;publicfunction__construct(CustomerId$customer_id,\DateTimeImmutable$create_at) {$this->customer_id =$customer_id;$this->create_at =$create_at; }publicfunctioncustomerId():CustomerId {return$this->customer_id; }publicfunctioncreateAt():\DateTimeImmutable {return$this->create_at; }}
Raise your event
useGpsLab\Domain\Event\Aggregator\AbstractAggregateEvents;finalclass PurchaseOrderextends AbstractAggregateEvents{private$customer_id;private$create_at;publicfunction__construct(CustomerId$customer_id) {$this->customer_id =$customer_id;$this->create_at =new \DateTimeImmutable();$this->raise(newPurchaseOrderCreatedEvent($customer_id,$this->create_at)); }}
Create listener
useGpsLab\Domain\Event\Event;class SendEmailOnPurchaseOrderCreated{private$mailer;publicfunction__construct(\Swift_Mailer$mailer) {$this->mailer =$mailer; }publicfunctiononPurchaseOrderCreated(PurchaseOrderCreatedEvent$event):void {$message =$this->mailer ->createMessage() ->setTo('recipient@example.com') ->setBody(sprintf('Purchase order created at %s for customer #%s',$event->createAt()->format('Y-m-d'),$event->customerId() ));$this->mailer->send($message); }}
Register event listener
services:SendEmailOnPurchaseOrderCreated:arguments:[ '@mailer' ]tags: -{ name: domain_event.listener, event: PurchaseOrderCreatedEvent, method: onPurchaseOrderCreated }
Publish events in listener
useGpsLab\Domain\Event\Bus\EventBus;// get event bus from DI container$bus =$this->get(EventBus::class);// do what you need to do on your Domain$purchase_order =newPurchaseOrder(newCustomerId(1));// this will clear the list of event in your AggregateEvents so an Event is trigger only once$events =$purchase_order->pullEvents();// You can have more than one event at a time.foreach($eventsas$event) {$bus->publish($event);}// You can use one method//$bus->pullAndPublish($purchase_order);
You do not need to specify the name of the event handler method. By default, the__invoke method is used.
useGpsLab\Domain\Event\Event;class SendEmailOnPurchaseOrderCreated{private$mailer;publicfunction__construct(\Swift_Mailer$mailer) {$this->mailer =$mailer; }publicfunction__invoke(PurchaseOrderCreatedEvent$event):void {$message =$this->mailer ->createMessage() ->setTo('recipient@example.com') ->setBody(sprintf('Purchase order created at %s for customer #%s',$event->createAt()->format('Y-m-d'),$event->customerId() ));$this->mailer->send($message); }}
Register event listener
services:SendEmailOnPurchaseOrderCreated:arguments:[ '@mailer' ]tags: -{ name: domain_event.listener, event: PurchaseOrderCreatedEvent }
Create subscriber
useGpsLab\Domain\Event\Event;useGpsLab\Domain\Event\Listener\Subscriber;class SendEmailOnPurchaseOrderCreatedimplements Subscriber{private$mailer;publicfunction__construct(\Swift_Mailer$mailer) {$this->mailer =$mailer; }publicstaticfunctionsubscribedEvents():array {return [ PurchaseOrderCreatedEvent::class => ['onPurchaseOrderCreated'], ]; }publicfunctiononPurchaseOrderCreated(PurchaseOrderCreatedEvent$event):void {$message =$this->mailer ->createMessage() ->setTo('recipient@example.com') ->setBody(sprintf('Purchase order created at %s for customer #%s',$event->createAt()->format('Y-m-d'),$event->customerId() ));$this->mailer->send($message); }}
Register event subscriber
services:SendEmailOnPurchaseOrderCreated:arguments:[ '@mailer' ]tags: -{ name: domain_event.subscriber }
InstallPredis withComposer, run:
composer require predis/predis
Register services:
services:# PredisPredis\Client:arguments:[ '127.0.0.1' ]# Events serializer for queueGpsLab\Domain\Event\Queue\Serializer\SymfonySerializer:arguments:[ '@serializer', 'json' ]# Predis event queueGpsLab\Domain\Event\Queue\Pull\PredisPullEventQueue:arguments: -'@Predis\Client' -'@GpsLab\Domain\Event\Queue\Serializer\SymfonySerializer' -'@logger' -'event_queue_name'
Change config for use custom queue:
gpslab_domain_event:queue:'GpsLab\Domain\Event\Queue\Pull\PredisPullEventQueue'
And now you can use custom queue:
useGpsLab\Domain\Event\Queue\EventQueue;$container->get(EventQueue::class)->publish($domain_event);
In latter pull events from queue:
useGpsLab\Domain\Event\Queue\EventQueue;$queue =$container->get(EventQueue::class);$bus =$container->get(EventQueue::class);while ($event =$queue->pull()) {$bus->publish($event);}
InstallPredis PubSub adapter withComposer, run:
composer require superbalist/php-pubsub-redis
Register services:
services:# PredisPredis\Client:arguments:[ '127.0.0.1' ]# Predis PubSub adapterSuperbalist\PubSub\Redis\RedisPubSubAdapter:arguments:[ '@Predis\Client' ]# Events serializer for queueGpsLab\Domain\Event\Queue\Serializer\SymfonySerializer:arguments:[ '@serializer', 'json' ]# Predis event queueGpsLab\Domain\Event\Queue\Subscribe\PredisSubscribeEventQueue:arguments: -'@Superbalist\PubSub\Redis\RedisPubSubAdapter' -'@GpsLab\Domain\Event\Queue\Serializer\SymfonySerializer' -'@logger' -'event_queue_name'
Change config for use custom queue:
gpslab_domain_event:queue:'GpsLab\Domain\Event\Queue\Subscribe\PredisSubscribeEventQueue'
And now you can use custom queue:
useGpsLab\Domain\Event\Queue\EventQueue;$container->get(EventQueue::class)->publish($domain_event);
Subscribe on the queue:
useGpsLab\Domain\Event\Queue\EventQueue;$container->get(EventQueue::class)->subscribe(function (Event$event) {// do somthing});
Note
You can use subscribe handlers as a services andtag itfor optimize register.
You can use many queues for separation the flows. For example, you want to handle events of different Bounded Contextsseparately from each other.
services:acme.domain.purchase_order.event.queue:class:GpsLab\Domain\Event\Queue\Pull\PredisPullEventQueuearguments: -'@Superbalist\PubSub\Redis\RedisPubSubAdapter' -'@GpsLab\Domain\Event\Queue\Serializer\SymfonySerializer' -'@logger' -'purchase_order_event_queue'acme.domain.article_comment.event.queue:class:GpsLab\Domain\Event\Queue\Pull\PredisPullEventQueuearguments: -'@Superbalist\PubSub\Redis\RedisPubSubAdapter' -'@GpsLab\Domain\Event\Queue\Serializer\SymfonySerializer' -'@logger' -'article_comment_event_queue'
And now you can use a different queues.
InPurchase order Bounded Contexts.
$event =newPurchaseOrderCreatedEvent(newCustomerId(1),new \DateTimeImmutable());$container->get('acme.domain.purchase_order.event.queue')->publish($event);
InArticle comment Bounded Contexts.
$event =newArticleCommentedEvent(newArticleId(1),newAuthorId(1),$comment new\DateTimeImmutable());$container->get('acme.domain.article_comment.event.queue')->publish($event);
Note
Similarly, you can split the subscribe queues.
This bundle is under theMIT license. See the complete license in the file: LICENSE
About
Library to create the domain layer of your DDD application