-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comparison with MediatR library #34
Comments
I think you are more specifically talking about CQRS, as CQS is a more general concept that talks about separating command methods from query methods. But what you're stating here is exactly my view on this topic, as I expressed in the past. But I'm not sure there is an overall consensus that this is the way to apply CQRS.
I am myself reluctant in using external libraries for parts that I consider core parts of my architecture. That's why I myself don't advice the use of MediatR. I want to be in complete control over the core interfaces. I expressed this on the MediatR forum. That said, using MediatR is not a bad thing, but just make sure that it's design perfectly aligns with what's best for your application or organization. If not, it's better to define the interfaces yourself. For instance, if you are seeing that command often need different cross-cutting concerns compared to queries, separating them in terms of interfaces might make more sense, as I expressed on the MediatR forum. In this case, be careful of the argument that this means you are redesigning MediatR yourself. This will hardly be the case. That's because 1. If you take a look in MediatR, you see there isn't a whole lot of code to begin with. It's mostly interfaces, and 2. a lot of the code (and its complexity) stems from the MediatR design decision to allow to be applicable to every DI Container out there (such as the very limited built-in .NET Core container). This is why recent versions moved away from decorators, and now use pipelines. This design seems solely chosen because some DI Containers don't have (good) support for decorators. So this is something else that should influence your decision. If your DI Container does have good support for decorators, a design that uses decorators is typically simpler and easier to comprehend and, therefore, makes more sense. But again, this is a decision that should be up to the architect, not enforced by an external library. Of course, it can make a lot of sense to reuse a well-known solution compared to a custom-made solution. For instance, you would typically use log4net or Serilog instead of creating your own library. Many developers are already familiar with these libraries. But I'm not sure that this argument holds for MediatR. MediatR is basically a set of interfaces, and whether or not they are placed in an external library or defined by the application itself, it doesn't change the fact that you need to understand why the purpose and benefit is of these interfaces. It's more an issue of understanding the underlying design principles. My experience is that developers who have worked with MediatR-like abstractions, have no problem in working in applications where command and query interfaces are separated—or the other way around.
This is a completely different topic :-). ASP.NET Core's |
Hi Steven Thanks for your excellent response, there is a rich seam of great comments to absorb and I feel I have a good understanding now. It'll make for an interesting debate at work. It helps to have a label and I wasn't sure how to refer to your approach, that why I wrongly used the term CQS. Our common approach has been to use .Net Core's built in IoC. Now I realise that MS.DI has no built-in support for Decorators and implementing a PoC looks like a challenge unless I use a different IoC. So to take the custom approach we might need to introduce a better IoC container. From this perspective it harder for me to make the argument to roll our own over using MediatR. I should also do some more reading in how Pipeline Behaviours differ from Decorators. Regards Stephen |
Are you saying that MediatR has the same 'decorated' behaviour for Commands and Queries? But instead we often want different aspects for Commands, audits, transactions etc to Queries. MediatR has an advantage in .Net Core with the limits of the built in MS.DI IoC container. Extra effort is required for 'lazy' devs to learn about IoC and DI ;) So I assume MediatR is doing some magic wiring taking over the role of the IoC, is this like a ServiceLocator? I'm now trying to wire up a proof of concept AoP example using SimpleInjector in an Azure Serverless Function App. Is there no way of hooking into an Application Start class or do I have to call the DIContainer from every function? I've been following your advice here: It would be great to have a full code example of AoP with SimpleInjector in a Function App similar to the book code download. |
TIP: The discussion in #30 might also be of value to you. |
As I said, you should pick the tools, patterns, and practices that align best with your organization. If this means picking MS.DI and MediatR, by all means, please do. I certainly won't judge. TIP: Document these architectural decisions for future developers and architects.
No, I'm not saying that, because I don't know the MediatR code base that well. You will have to investigate how to differentiate your cross-cutting concerns over commands and queries when using MediatR.
But it does so at the cost of (IMO) design. In earlier releases, MediatR used the decorator pattern (IMO clearly indicating that Jimmy preferred the decorator design pattern over the chain of responsibility pattern), but only stopped doing that after he wanted to support 'less mature' DI Containers.
There is little "magic wiring" going on in MediatR. It's repo contains documentation that shows you how to register your components in your favorite DI Container.
It's certainly true that the I would say that, since MediatR is the framework that delegates your code, I would consider it part of the Composition Root, even though, from a assembly-dependency perspective it's officially not (as the |
As mentioned by @CasperWSchmidt in 839, you can inject dependencies into your Azure function class. This allows you to inject the public record YourFunction(IMediator mediator) // Excuse my C# 9.0
{
[FunctionName("YourFunctionName")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/yourFunctionName")]
HttpRequest req)
{
await mediator.Execute(new MyCommand { ... });
}
} In the [assembly: FunctionsStartup(typeof(Startup))]
namespace YourFunction.DI
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var configuration = builder.GetContext().Configuration;
var simpleInjectorContainer = Setup(configuration);
builder.Services.AddSingleton<IMediator>(
new Mediator(simpleInjectorContainer.GetInstance));
}
// Could be non-static
public static Container Setup(IConfiguration configuration)
{
//This is where the container is set up and verified
}
}
} WARNING: The |
For my PoC I've injected the Startup defined SI Container into my function's constructor and resolved each dependency inside the function. This works quite well but feels awkward compared to Constructor Injection and doesn't appear testable. var createTicketService = _container.GetInstance<ICommandService<CreateTicket>>();
var _queryTickets = _container.GetInstance<IQueryTickets>(); I can't see how to implement your last example with the IMediator wrapper around the SimpleInjectorContainer Instance. And am worried I'm making it too difficult by implementing an external IoC in an Azure Function App. Function Apps being relatively new, still don't have good support for using external IoC Containers other than MS.DI and I couldn't find much written on this, nor an approach that works as nice as in a regular WebAPI app. So my PoC doesn't grow too big a task, I'm going to demonstrate the custom approach with SI in a WebAPI application and show the limitations of DI in Function Apps. Our architect, has fallen in love with Function Apps! I'm thinking MediatR might work really well for a less complex domain when using an Azure Function App. However for a more complex domain I'm favouring the custom approach you have shown, built on a WebAPI AppService. The custom approach allows nuanced decisions, to handle Commands AoP separately, choice of simple single interfaces or a Mediator for Queries and combined with a separate process for composing Domain Events. Thanks once again |
@bluezebra I'm not sure what you mean by "awkward" and not being testable. If you do as suggested in #839 and let your function be a humble object all it does is resolve the handler and call it which is two lines of code with no logic that needs testing. All your business logic should be in the handler and perhaps some decorators 😊 which are all testable. We have this exact design for multiple azure functions already in production. @dotnetjunkie I had a quick look at the ASP.NET extension with auto-bridging between Microsoft DI and Simple Injector, but couldn't figure out how to do a similar thing for azure functions, which would be really cool as that means the functions can be the handlers themselves (at least for some of the trigger types like http) |
I just meant the functions themselves were not testable or I don't know how to, for example testing for returning http codes like bad request, success etc. Not much info I could find, most show only Ms.di. thanks for looking into this. It would be a great issue to solve. |
IMHO it shouldn't be necessary to have tests if the function simply looks like this
There's not really any business logic in there that needs testing. If you insist, you can depend on IContainer and simply mock/stub the |
@bluezebra, whenever possible, prevent implementing returning of custom HTTP codes inside your Azure functions. Instead, try an approach as I demonstrate in this repository. That is: try to implement the returning of HTTP error codes in a maintainable fashion, where you have a small piece of infrastructure (e.g. a decorator, extension method, or the like) that maps exceptions or command results to HTTP error codes.
@CasperWSchmidt if you means by "ASP.NET extension with auto-bridging" the Simple Injector extension packages for ASP.NET Core, they rely on the existence of seams/interception points, usch as |
Hello Steven
Please could you check my reasoning.
I'm trying to compare the popular MediatR library with your separate Command and Query Mediator pattern. My colleagues are keen on adopting MediatR and I want to understand the subtle differences.
CQS separately handles Commands and Queries, with an optional Mediator for Queries to reduce dependencies as they are usually more numerous. Commands are usually more isolated, so less issue with over injection, thus do not require the additional Mediator which would obfuscate dependencies. Having both Command and Query dependencies abstracted to a Mediator might be good for decoupling, but could be considered an antipattern in hiding dependencies.
MediatR has less distinction between Command and Queries. Confusingly both are called Requests, and does not enforce void/unit on Commands. However some Commands might be permitted to return a Result type if desired for flexibly handling success or failure in a more functional way. Relying on a library instead of rolling your own is a dependency in itself however it might be easier for other developers to understand.
Then there is .Net Core’s FromServices attribute for injecting directly to Actions and relieving the Constructor overloading problem and perhaps pointing towards the simpler explicit Query injection. Thus having the freedom to roll your own is more flexible.
Thanks for an inspiring DI book, articles and comment responses.
Regards
Stephen
The text was updated successfully, but these errors were encountered: