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<TResponse>(IRequest<TResponse> request);
Task<TResponse> SendAsync<TResponse>(IAsyncRequest<TResponse> request);
void Publish<TNotification>(TNotification notification) where TNotification : INotification;
Task PublishAsync<TNotification>(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.
Async and await don't mix perfectly with traditional OOP. I have a blog series on the subject; you may find the post on async interfaces helpful in particular (though I don't cover anything you haven't already discovered).
The design problems around async
are extremely similar to the ones around IDisposable
; it's a breaking change to add IDisposable
to an interface, so you need to know whether any possible implementation may ever be disposable (an implementation detail). A parallel problem exists with async
; you need to know whether any possible implementation may ever be asynchronous (an implementation detail).
For these reasons, I view Task
-returning methods on an interface as "possibly asynchronous" methods, just like an interface inheriting from IDisposable
means it "possibly owns resources."
The best approach I know of is:
Task
/Task<T>
).Task.FromResult(...)
for synchronous implementations. This is more proper than an async
without an await
.This approach is almost exactly what you're already doing. A more ideal solution may exist for a purely functional language, but I don't see one for C#.
Not really an answer, but for what it's worth, i came to the exact same conclusions, and a very similar implementation.
My ICommandHandler<T>
and IQueryHandler<T>
return Task
and Task<T>
respectively. In case of a synchronous implementation i use Task.FromResult(...)
. I also had some *handler decorators in place (like for logging) and as you can imagine these also needed to be changed.
For now, i decided to make 'everything' potentially await-able, and got into the habit of using await
in conjunction with my dispatcher (finds handler in ninject kernel and calls handle on it).
I went async all the way, also in my webapi/mvc controllers, with few exceptions. In those rare cases i use Continuewith(...)
and Wait()
to wrap things in a synchronous method.
Another, related frustration i have is that MR recommends to name methods with the *Async suffix in case thay are (duh) async. But as this is an implementation decision i (for now) decided to stick with Handle(...)
rather than HandleAsync(...)
.
It is definitely not a satisfactory outcome and i'm also looking for a better solution.
You state:
the consumer of a handler shouldn't be aware of the fact that a command/query handler is sync or async as this is a cross cutting concern
Stephen Clearly already touched this a bit, but async is not a cross-cutting concern (or at least not the way it's implemented in .NET). Async is an architectural concern since you have to decide up front to use it or not, and it completely chances all your application code. It changes your interfaces and it's therefore impossible to 'sneak' this in, without the application to know about it.
Although .NET made async easier, as you said, it still hurts your eyes and mind. Perhaps it just needs mental training, but I'm really wondering whether it is all worth the trouble to go async for most applications.
Either way, prevent having two interfaces for command handlers. You must pick one, because having two separate interfaces will force you to duplicate all your decorators that you want to apply to them and duplicates your DI configation. So either have an interface that returns Task
and uses output properties, or go with Task<TResut>
and return some sort of Void
type in case there is no return type.
As you can imagine (the articles you point at are mine) my personal preference is to have a void Handle
or Task Handle
method, since with commands, the focus is not on the return value and when having a return value, you will end up having a duplicate interface structure as the queries have:
public interface ICommand<TResult> { }
public interface ICommandHandler<TCommand, TResult>
where TCommand : ICommand<TResult>
{
Task<TResult> Handle(TCommand command);
}
Without the ICommand<TResult>
interface and the generic type constraint, you will be missing compile time support. This is something I explained in Meanwhile... on the query side of my architecture