jorge07/symfony-6-es-cqrs-boilerplate

Domain operations

Closed this issue ยท 8 comments

mxkh commented

Hi @jorge07!

Your boilerplate is fantastic! I have a question regarding the domain operations.

For example, after UserWasCreated, who should be responsible for sending a notification (email, push, SMS) after the persistence layer successfully finished his work? Should we have the Handler class that will work with the event bus as a part of the domain layer, which will expect the UserWasCreated event in the __invoke method? I spied this approach in botilka demo

Also, it would be nice to add more advanced examples to the boilerplate. Currently, I am working on investigating DDD for my organization and I might contribute if you don't mind.

Thanks!

Hi @mxkh

Your approach makes totally sense to me in terms of workflow.
An event listener class / interface that implements/extends Broadway\EventHandling\EventListener in your domain layer and the implementation for the transport (mail, push, sms...) in infrastructure. Keep in mind I use Broadway and method to implement here is handle instead __invoke as you may expect but there're a reason and it's in the read model event handling side: https://github.com/broadway/broadway/blob/master/src/Broadway/ReadModel/Projector.php#L36

Happy to add more advanced examples and improve documentation. Open for suggestions ๐Ÿ‘

The component Messenger of Symfony isn't sufficient ? Because it do that no ?

@xfifix the messenger component is used for the Command, Query and AsyncEventBus but Broadway has his own even bus too but ONLY for synchronous tasks. So yes, you're right and I miss the topic here that if you want to to this in an async way you've to implement this interface and will be automatically executed in the workers

That' makes me realise there's a lot of room for improvement in the documentation. Thanks!

mxkh commented

@jorge07 Thanks for your reply! It was helpful!

So, if I want to send a notification, where should I do that according to your boilerplate? I want to clarify how to use it properly. Should I work with the read model events? Because I want to make sure that the persistence layer finished successfully and only then send UserWasCreated notification to some transport

You've an aggregate root called User, when the user is created it generates a UserWasCreated event. Then it get persisted in the write model (events gets persisted). Then this event it's published in the event bus and you've some projections, like the UserView in this repo, that generates the read model. This projection handlers extend Broadway Projection so they will be executed synchronously. You can put your SMS handler on the async event bus to guarantee it gets executed when read model is already applied. Otherwise you'll need to deal with eventual consistency

mxkh commented

You've an aggregate root call User, when the user is created it generates a UserWasCreated event. Then it get persistent in the write model (events gets persisted). Then this event it's published in the event bus and you've some projections like the UserView in this repo that generates the read model. This projection handlers extend Broadway Projection so they will be executed synchronously. You can put your SMS handler on the async event bus to guarantee it gets extended when read model is already applied. Otherwise you'll need to deal with eventual consistency

Thanks for the detailed answer! Your words confirm what I was thinking about but I wanted to get proof of my thoughts ๐Ÿ˜… Thank you a lot! I am going to fork your boilerplate and add more examples.

Lolz I realized how poorly written was my response, sorry, I was walking with the kid and the autofixes in the phone keyboard doesn't behave very well with Spanish and English languages enabled at the same time

I'm refactoring few things and playing with advance things with symfony messenger.
It's all in #187 PR
There's an example of how to deal with async events here with some notes about the things i found and work around
https://github.com/jorge07/symfony-5-es-cqrs-boilerplate/pull/187/files#diff-7a5e0582aaf2f0e794df52345439b794