I recently saw some code scenarios where CommandHandlers were being injected with ICommandExecutor to call other commands. So commands within commands. This was also true for some QueryHandlers being injected with IQuery.
public class UpdateCarDetailsCommandHandler : CommandHandler<UUpdateCarDetailsCommand>
{
private ICommandExecutor _command;
public UpdateCarDetailsCommandHandler (ICommandExecutor command)
{
_command = command;
}
public override void Execute(UpdateCarDetailsCommand command)
{
//do something with repository
_command.Execute(new CarColour())
}
}
This seems incorrect to me, as the ICommandExecutor would be the composition root in this scenario. Just wondered people thoughts on this?
I say you are correct to be wary of using commands and queries within other commands and queries. Beware abstractions that do too much.
The S in CQRS stands for Segregation. This clearly implies that Commands should remain separate from other Commands and Queries should remain separate from other Queries. But can queries be used by commands? As always, it depends.
Udi Dahan's article from 2009 suggests not:
Since your queries are now being performed off of a separate data store than your master database, and there is no assumption that the data that’s being served is 100% up to date, you can easily add more instances of these stores without worrying that they don’t contain the exact same data.
Dino Esposito recommends using separate projects:
Applying CQRS means you’ll use two distinct middle tiers. One tier takes care of commands that alter the system state. The other retrieves the data. You create a couple of class library projects—query stack and command stack—and reference both from the main Web server project.
My personal view is that you should think of these standard Command and Query handlers as holistic abstractions. A holistic abstraction is an abstraction that is concerned with the whole transaction; it cannot be a dependency within something larger than itself within the confines of a single transaction.
What is needed instead are similar pairs of abstractions that are injectable strategies and that can be decorated with cross-cutting concerns.
E.g.
public interface IDataCommandHandler<TCommand> where TCommand : IDataCommand
{
void Handle(TCommand command);
}
public interface IDataQueryHandler<TQuery, TResult> where TQuery : IDataQuery<TResult>
{
TResult Handle(TQuery query);
}
Or
public interface ICommandStrategyHandler<TCommand> where TCommand : ICommand
{
void Handle(TCommand command);
}
public interface IQueryStrategyHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
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