Myself, I have a MVC Controller action to receive the command (from nearly any source that can do an HTTP POST):
[HttpPost]
public ActionResult Execute([ModelBinder(typeof(CommandModelBinder))] ICommand command)
{
CommandResult result = CommandDispatcher.Dispatch(command);
return Json(result);
}
The custom model binder (CommandModelBinder) takes care of shoving the POST information into an appropriate .NET object, using reflection and a type hint from the POST data. CommandResult has 2 properties, an enum for success / failure, and a string message. ICommand has a Validate method which also returns CommandResult. The Dispatcher is basically the "local command bus":
readonly Dictionary<Type, object> commandHandlers;
public CommandResult Dispatch(ICommand command)
{
CommandResult result = command.Validate();
if (result.Status > ResultType.Error)
{
try
{
Invoke(command as dynamic);
result = new CommandResult(ResultType.Success);
}
catch (MyCustomDomainException ex)
{
result = new CommandResult(ResultType.Error, ex.Message);
}
}
return result;
}
private void Invoke<T>(T command) where T : ICommand
{
IHandlesCommand<T> handler = (IHandlesCommand<T>)commandHandlers[typeof(T)];
handler.Handle(command);
}
All the reflection junk to initialize the commandHandlers variable is in the omitted constructor. But it's basically as you would expect: looking through the assembly for all classes that inherit IHandlesCommand, getting the generic type parameter, and making a dictionary of it all.
In my system, the event handlers are run asynchronously. (I just queue them to run on the ThreadPool.) At that point, I know that the command has run successfully, and there's really no need for me to wait any longer before returning a result.
This isn't really a distributed application, and it's not setup for massive scale, although it could be modified to do so if the need arises. Instead, I wanted to take advantage of a) messaging to divide work and have multi-platform capability in the future (especially client-side), b) CQS to avoid the domain bloat that occurs when using the domain model for a view model (or alternatively, to avoid a lot of mapping code maintenance), and c) use DDD to deal with complex requirements.
I think a lot of the talk on async commands has to do with distributed systems, because the commands may literally go to a queue to await execution on some remote machine that the client may not know ahead of time. At that point, it could be pretty challenging to wait for commands synchronously. Queued commands where only one command per AR runs at a time have pretty nice advantages as far as avoiding locking performance overhead and resolving concurrency conflicts. These are nice architectural properties for that class of application, but it's not what many of us need to worry about preeminently.