gnugat / gnugat.github.io

Blog about technical and practices advices
https://gnugat.github.io/
21 stars 12 forks source link

command bus #70

Closed FlorianCcj closed 5 years ago

FlorianCcj commented 6 years ago

the article

https://github.com/gnugat/gnugat.github.io/blob/master/_sculpin/source/_posts/2016-05-11-towards-cqrs-command-bus.md

is unbelievable, at last an article which clarify those concept. Thanks for all.

just I wondered the command bus

$createdProfile = $this->get('command_bus')->handle(new CreateNewProfile(
        $request->request->get('name')
));

what does it look like ? and how do it take care of the middleware ?

Thanks for all again, this article is unbelivable and so clear.

gnugat commented 6 years ago

Hi @FlorianCcj, I'm glad I can help!

If you consider the following interfaces:

<?php

interface CommandHandler
{
    public function supports($command): bool
    public function handle($command);
}

interface CommandBus
{
    public function register(CommandHandler $commandHandler);
    public function handle($command);
}

You can have a "base" CommandBus implementation that registers CommandHandlers, and then when given a Command will try to find a Handler that supports it:

<?php

class RegisteringCommandBus implements CommandBus
{
    private $commandHandlers = [];

    public function add(CommandHandler $commandHandler)
    {
        $this->commandHandlers[] = $commandHandler;
    }

    public function handle($command)
    {
        foreach ($this->commandHandlers as $commandHandler) {
            if ($commandHandler->supports($command)) {
                return $commandHandler->handle($command);
            }
        }
        throw new NotSupportedException('The given Command is not supported by any registered CommandHandlers.');
    }
}

For most use cases, this is enough, but sometimes you want to always execute some code before or after all or some CommandHandlers, which is where the "Middleware" pattern is used: you create an implementation that does that extra task, and you inject in it the "base" implementation so you can call it before or after that task.

For example here's a CommandBus implementation that always log a message after any command handler have been executed:

<?php

class LogCommandBus implements CommandBus
{
    private $commandBus;
    private $logger;

    public function __construct(
        CommandBus $commandBus,
        Logger $logger
    ) {
        $this->commandBus = $commandBus;
        $this->logger = $logger;
    }

    public function handle($command)
    {
        $value = $this->commandBus->handle($command);
        $this->logger->log("C'est pas faux");

        return $value;
    }
}

In most of my use cases, I have no use for Middleware, so I usually skip the CommandBus entitrely but still stick to the Command / CommandHandler. You can read more about this here: http://gnugat.github.io/2017/09/20/pragmaticlean-command-bus.html

I hope this is useful, let me know if you have more questions.