CQRS - Executing Commands in Commands
I recently saw some code scripts where CommandHandlers injected ICommandExecutor to invoke other commands. Thus, teams are within teams. This is also true for some QueryHandlers who have introduced 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 wrong to me since ICommandExecutor will be the root of the composition in this scenario. People just wondered about it?
source to share
I say that you are right for fear of using commands and queries in other commands and queries. Beware of abstractions that do too much.
The S in CQRS stands for Segregation . This clearly means that Commands should remain separate from other commands and requests, which should remain separate from other requests. But can queries be used by commands? As always, it depends.
The Udi Dahan article from 2009 does not suggest:
Since your queries are now running separate from the datastore than your main database, and there is no assumption that the data being served is 100% so far, you can easily add more instances of these stores without worrying that they are not accurate. ...
Dino Esposito recommends using separate projects:
Applying CQRS means that you are using two separate middle levels. One level serves commands that change the state of the system. Another retrieves the data. You create multiple class library projects - the request stack and the command stack - and reference them as the main web server project.
My personal opinion is that you should treat these standard command and request handlers as holistic abstractions . A holistic abstraction is an abstraction associated with an entire transaction; he cannot be dependent on anything more than himself within a single transaction.
What is needed instead is similar pairs of abstractions that are injection strategies that can be embellished with cross-cutting problems.
eg.
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);
}
source to share