I went with a variant of the suggested solution; keeping all ICommandHandler
s and IQueryHandler
s potentially aynchrono
I created a project for just this - I wound up not splitting commands and queries, instead using request/response and pub/sub - https://github.com/jbogard/MediatR
public interface IMediator
{
TResponse Send(IRequest request);
Task SendAsync(IAsyncRequest request);
void Publish(TNotification notification) where TNotification : INotification;
Task PublishAsync(TNotification notification) where TNotification : IAsyncNotification;
}
For the case where commands don't return results, I used a base class that returned a Void type (Unit for functional folks). This allowed me to have a uniform interface for sending messages that have responses, with a null response being an explicit return value.
As someone exposing a command, you explicitly opt-in to being asynchronous in your definition of the request, rather than forcing everyone to be async.