I've been using a pattern very similar to what is described in this excellent article to have commands and queries as objects. I am also using SimpleInjector as the DI container.
The only significant difference is that rather that controller take an explicit dependency on some ICommandHandler<TCommand>
I want the controllers to take a dependency on an object (a Dispatcher
) which will take a ICommand
instance and resolve the correct handler for that command. This will reduce the number of parameters that the constructors need to take and make the whole thing a little easier to use.
So my Dispatcher
objects constructor looks like this:
public CommandAndQueryDispatcher(IEnumerable<ICommandHandler> commandHandlers,
IEnumerable<IQueryHandler> queryHandlers)
{
}
and my CommandHandler interfaces look like this:
public interface ICommandHandler<in TCommand> : ICommandHandler
where TCommand : ICommand
{
void Execute(TCommand command, ICommandAndQueryDispatcher dispatcher);
}
public interface ICommandHandler
{
void Execute(object command, ICommandAndQueryDispatcher dispatcher);
}
And a typical command handler looks like:
public abstract class CommandHandlerBase<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
public abstract void Execute(TCommand command, ICommandAndQueryDispatcher dispatcher);
public void Execute(object command, ICommandAndQueryDispatcher dispatcher)
{
Execute((TCommand) command, dispatcher);
}
}
internal class DeleteTeamCommandHandler : CommandHandlerBase<DeleteTeamCommand>
{
public DeleteTeamCommandHandler(){ }
public override void Execute(DeleteTeamCommand command,
ICommandAndQueryDispatcher dispatcher)
{
... functionality here...
}
}
However this change had some knockons and now I want to add some decorators to my commands and queries I've having some problems.
In order to have all of the commands and queries injected into the Dispatcher
I made them all have a base, genericless, interface ICommandHandler
and IQueryHandler
, then interrogate the instances actually received (which are generic) to get the type of command they handle to register them so I can look up the handler based on the type of the given command later on.
Now when I try and use decorators as indicated in the examples I can't seem to get anything injected into my Dispatcher
, as the decorated instances are registered as generic types, so don't get resolved as basic ICommandHandler
instances. If I try making the decorators non generic then the instances which are injected don't have any generic type parameters so I can't find what type of command its a handler for.
I feel like I must be missing something fairly simple.
So my question is either
Dispatcher
?OR
This will reduce the number of parameters that the constructors need to take and make the whole thing a little easier to use
Please take a close watch on this, because by doing this, you might hide the fact that your controllers do too much; violate the Single Responsibility Principle. SRP violations tend to lead to maintainability issues later on. There's even a follow up article of the author of the article you refer to (that's me btw) that states:
I certainly wouldn’t advocate an ICommandProcessor [that's ICommandAndQueryDispatcher in your case] for executing commands - consumers are less likely to take a dependency on many command handlers and if they do it would probably be a violation of SRP. (source)
The article even discusses a solution for this for queries, but you can apply it to your commands as well. But you should consider stripping your solution down and remove the non-generic interfaces. You don't need them.
Instead, define the following:
public interface ICommandHandler<TCommand> : where TCommand : ICommand
{
void Execute(TCommand command);
}
Do note a few things:
ICommandAndQueryDispatcher
. That's just ugly. The ICommandAndQueryDispatcher
is a service and services need to be passed through constructor injection. The command
on the other hand is runtime data, and runtime data is passed through using method arguments. So if there's a command or query handler that needs the dispatcher: inject it through the constructor.in
keyword for TCommand
. Since commands are use cases, there should be a one-to-one mapping between a command and a command handler implementation. Specifying an 'in' however means that one command class can map to multiple handlers, but this should not be the case. When dealing with events and event handlers on the other hand, this will be a much more obvious approach.CommandHandlerBase<TCommand>
. You don't need that. I would argue that a good design hardly ever needs a base class.Another thing, don't try to mix the dispatcher for commands with that for the queries. Two responsibilities means two classes. This is how your command dispatcher will look:
// This interface is defined in a core application layer
public interface ICommandDispatcher
{
void Execute(ICommand command);
}
// This class is defined in your Composition Root (where you wire your container)
// It needs a dependency to the container.
sealed class CommandDispatcher : ICommandDispatcher
{
private readonly Container container;
public CommandDispatcher(Container container) {
this.container = container;
}
public void Execute(ICommand command) {
var handlerType = typeof(ICommandHandler<>)
.MakeGenericType(command.GetType());
dynamic handler = container.GetInstance(handlerType);
handler.Handle((dynamic)command);
}
}
Do note that you don't inject a collection of command handlers here, but instead to request a handler from the container. This code will only contain infrastructure and no business logic, so if you place this implementation close to the code that is responsible of wiring the container, you will not abuse the Service Locator anti-pattern, and this is a valid approach. The rest of the application in that case still doesn't depend on the DI framework.
You can register this CommandDispatcher
as follows:
container.RegisterSingle<ICommandDispatcher>(new CommandDispatcher(container));
If you take this approach, because you request a handler by the ICommandHandler<TCommand>
interface, the container will automatically wrap the handlers with any decorators that must be applied according to your configuration and the generic type constraints you applied.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With