What about having a class being responsible for handling more than one request?
Imagine having 2 different requests:
record Ping : IRequest<string> { }
record Echo(string Message) : IRequest<string>;
Defining a handler able to handle both the types of request with MediatR is as simple as implementing both IRequestHandler<Ping, string>
and IRequestHandler<Echo, string>
:
class MyHandler :
IRequestHandler<Ping, string>,
IRequestHandler<Echo, string>
{
public Task<string> Handle(Ping request, CancellationToken cancellationToken)
{
return Task.FromResult("Pong");
}
public Task<string> Handle(Echo request, CancellationToken cancellationToken)
{
return Task.FromResult(request.Message);
}
}
The plain OOP implementation is equally simple:
interface IMyHandler
{
string Ping();
string Echo(string message);
}
class MyHandler : IMyHandler
{
string IMyHandler.Ping() => "Pong";
string IMyHandler.Echo(string message) => message;
}
Answer
Compare the interface of MyHandler
with MediatR:
Task<string> Handle(Ping request, CancellationToken cancellationToken);
Task<string> Handle(Echo request, CancellationToken cancellationToken);
and without:
string Ping();
string Echo(string message);
The naming in the OOP interface is domain-driven; With MediatR the code tends to contain many repetitions of Send
and Handle
, which are a missed opportunity to use a business-driven, obiquitous language. The reference to domain notions is moved from the method name to the parameters. This once again how MediatR is basically a re-implementation of the native C# method dispatching.
It is also interesting to compare the 2 class types, and to notice how in one notions from the domain such as Ping
and Echo
mix with notions from the infrastructure, inherited from an external library:
class MyHandler :
IRequestHandler<Ping, string>,
IRequestHandler<Echo, string>
and how the other is instead purely domain-driven:
class MyHandler :
IMyHandler