webdevilopers / php-ddd

PHP Symfony Doctrine Domain-driven Design
201 stars 11 forks source link

Using the Symfony Event Dispatcher for Application Events #21

Open webdevilopers opened 8 years ago

webdevilopers commented 8 years ago

Here is a question by @chrisguitarguy that came to mind before when I was using the @SimpleBus by @matthiasnoback:

Do you hide your event emiting code behind an interface you own in your #php apps? Why or why not? By that, I mean instead of using Symfony's event dispatcher directly you create an interface and a Symfony adapater implenting it.

The @SimpleBus has a SmyfonyBridge:

And for the RabbitMQBundle too:

I remember trying out the tutorial:

But I think I didn't figure out why the events did not actually fire.

Does anybody have a working example or code on github?

chrisguitarguy commented 8 years ago

The use case I had in mind for the question was a bit different than the simple bus example. I have a set of distributed workers that all tackle on task in parallel. When a worker completes its bit of the task, there's a good chance that it may be the last bit of the overall task as a whole. If that's the case, the worker should emit an event. This is a domain event saying that something happens and it is emitted from a service object, not a command handler.

In the end I decided to try out having an internal event emitter interface, but it's a super leaky abstraction. Strikes the right balance for service that need to emit events only and don't care how they are handled, though.

use Symfony\Component\EventDispatcher\EventDispatcherInterface;

// this has a different name in the real application
interface DomainEvent
{

}

interface Emitter
{
    public function emit(DomainEvent $event);
}

final class SymfonyEmitter implements Emitter
{
    /**
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function emit(CrmEvent $event)
    {
        $this->dispatcher->dispatch(get_class($event), $event);
    }
}
matthiasnoback commented 8 years ago

I don't know if you're waiting for my opinion, but here we go anyway: I don't like to use the Symfony event dispatcher for my domain events (because of the Event base class stuff and the way event listeners can stop propagation, have a priority, etc.), so I added a very simple dispatcher to SimpleBus, which has no requirements whatsoever for the contract of the objects involved.

webdevilopers commented 8 years ago

Thanks @matthiasnoback for your feedback!

I havn't checked it yet but I think there is another downside of the @symfony event dispatcher since it only allows passing a single object instead of multiple primitive args - e.g. comming from a Command or DTO. Is that right?

matthiasnoback commented 8 years ago

That's right, but I think it's always a sensible thing to dispatch an object anyway.

webdevilopers commented 7 years ago

I saw your example how to inject the event_recorder into your Application Handler here @matthiasnoback :

But as mentioned I didn't understand how / where to put my middleware - or how to use it with a Symfony Bridge. For now I could release the events inside my Controller which is very ugly.

Besides I would prefer to raise the event inside my DomainModel. - But without using Doctrine. Is this possible with your Bus / Bundles?

Maybe @mablae had something like this running already?

webdevilopers commented 7 years ago

Related: