Closed albe closed 8 years ago
I agree for many case 1 HTTP Request === 1 Command
But definitively not in all cases, dispatching multiple command in a single request can make sense. And if we are bound the 1 HTTP Request === 1 Command we are locked down.
That's an interresting idea - do you have any concrete use-case for sending multiple Commands in a single request?
I mean, a command is generally supposed to trigger a single transaction (talk to a single Aggregate) and triggering multiple transactions within a single request is not that straightforward - what if one transaction in between fails? Will the whole request fail or still return 200 OK?
Don't have practical example, but try to be the devil's advocate a bit ;)
See also our discussion here: https://discuss.neos.io/t/cqrs-es-meeting-notes-06-09-2016/1392 Right now also think that we don't need a command bus, but we should have some central manager which accepts commands. And if you agree on that, we can just use a Command Bus class for that and give it a dispatch() method.
I would not feel comfortable using the existing Router or Dispatcher in Flow for commands or events. Both serve a different purpose.
I can't think of a scenario where you want to have two commands within one request, IMO that's rather dangerous and should probably replaced by a ProcessManager
.. Anyways, it's got nothing to do with this issue IMO because you can dispatch as many commands as you wish, whether you have a CommandBus
or not – or am I missing something?
We just discussed the CommandBus
in our initial CQRS Meeting and decided:
Unless someone comes up with a valid counter-argument we'll remove the CommandBus
for now and see how far we get without it. Before releasing the package we'll re-evaluate that decision.
To recap on some of the potential use-cases for a CommandBus
and (my personal) counter-arguments in relation to Flow:
This is considered bad practice and most libraries will fault if there's none or more than one matching handler for a given command
According to Greg Young: "asynchronous commands don't exist you want an event"
This can be useful in order to add validation or logging in regular implementations.
In Flow we can already syntactically validate the command when it's sent to the Controller (using @Flow\Validate
annotations and/or constructor assertions).
Logging all commands would also be easily achievable using AOP (within(CommandControllerInterface) && method(.*->handle.*())
) – IMO one of the very rare valid use cases for it.
I can't really imagine that this would be a useful extension point tbh, because the handler usually just
Anyways, if an extension point is needed, one could still refer to some *HandlerInterface
and replace that via Objects.yaml
if needed.
I like :) Thx for the write-up!
I assigned this to you, @dfeyer, because you volunteered to remove the CommandBus code, right?
I assigned this to you, @dfeyer, because you volunteered to remove the CommandBus code, right?
@robertlemke I volunteer to remove anything ;)
In Slack @dfeyer argued:
I need a command line utility to launch some simple commands ... and now I see the need of a commandbus
I can see that it could be useful to be able to execute all available commands from a very generic CLI command controller. At least in slightly more complex scenarios I don't think that will work out and a dedicated command controller makes sense IMO. In any case it should be possible to implement that generic command controller nevertheless as we can determine all commands and their handlers.
The "CommandBus" is actually rather a kind of router/dispatcher, as it will only dispatch a command to a single handler. Also, the CommandBus is commonly used in client applications, since there is nothing like a http request involved and all communication happens in-process. However, for our web-based environment, there is no real need for a CommandBus, since the Flow Router+Dispatcher can already act as the "CommandBus".
Either the request URI describes the command type directly and the controller actions act as CommandHandler, or a single dispatch controller action is involved, which will invoke the specific handlers.