Why you shouldn’t use MediatR in .NET

Bending the Clean Architecture Principles

Introduction

So you’re writing code using .NET, and you surely follow the Clean Architecture and its layered abstractions. Apparently and even though the concept was introduced long ago, this is getting very trendy now.

One of those is the famous MediatR library. Which is typically used to separate your application’s logic from the Presentation layer. Your presentation layer could be a .NET Web API for instance. Another advantage is the separation of Reads and Writes (CQRS) that comes from MediatR.

So with all those advantages, where is the problem with this approach and why you shouldn’t use it ?

The Problem

Here’s the classic, everything’s a tradeoff. But here’s the thing, when I asked people around on why they are using the mediator pattern the only argument they gave me is ‘Because its cleaner, it abstracts the business logic’.

And I get it, you want to abstract your business layer from the framework itself. But have you thought, researched on how you can do that? While this can work with the Mediator pattern, why did it become the industry standard for each and every API?

  • You don’t necessarily need to separate your reads and writes. This is subject to another topic but you should only use CQRS when you want to scale those two differently.
  • You don’t need to create queries and commands and add that layer to your application.
  • Testing with MediatR is also tricky due to this decoupling factor
  • Code understanding and maintainability is also hit. You will need to go back and forth with your debugger, explain more for newcomers how this magic is happening…

Now you did all that and fought this battle, but you get somewhere where you need some sort of business validation. You already validated your API contracts but I am talking more about a Domain validation concept. Your smart ass says: “Oh, I’m going to do that in my application layer thus MediatR”. The idea is not wrong, you will most likely implement a middleware pipeline behaviour to validate your business models before handling the requests. Then you will top that with the famous FluentValidation library. You’ll soon realise that you need proper exception handling, logging, caching or anything related and here it comes:

You found yourself reinventing the wheel on multiple occasions, having both a middlewares and application middlewares, abstracting and creating a lot of code from what can be ‘Out of the Box’ for an application that does not necessarily need that much. So you are putting this extra effort for this defined benefit whereas you should only consider it when you need the scalability and modeling benefits you get.

But this should not be the standard ‘go-to’ solution for a clean architecture project. Your intent can be expressed by alternative solutions that is considered a middle ground between fully separated and isolated boundaries and a highly coupled codebase. Here’s some solutions:

Solution 1: The Old Standard Way

You can easily abstract your business logic within a couple of interfaces, a static class or anything offered by the world of OOP. This is the most adequate if you have a small codebase because it will decouple your logic from your Controllers while keeping this reference and everything that comes with it from debugging to testing and maintainability. You can also use all the out of the box tools provided in your API’s framework like logging and validation. And it is also scalable in its context, if you just want to add more endpoints and features…

Solution 2: The Minimal Way

This is considered new with the Minimal API’s feature added in .NET 6. Microsoft basically refactored the old ControllerBase behaviour from MVC to support an easier way to create an API in .NET, but what does that gives us?

Well, let’s say that now, your endpoint is only linked to a standard C# function. This is basically Microsoft handing you the power to separate your concerns as you please. Each route has only 1 function, so it is definitely scalable on the long run, you can easily refactor, group functions as you want. You can create extensions and abstractions to automatically do that, you can inject only what you need in this single endpoint… All that while having access to most of the MVC toolbox.

Your API will perform faster as it is not loading all those MVC components that you never use, and you’re left of with C# functions with no magic behind the scene. Everyone understand your code directly and can craft as needed.

Solution 3: The Minimalist Enthusiast

Let’s add a twist to the second solution: Instead of using MediatR, have you tried using FastEndpoints in the first place? This Open Source library is awesome for delivering an approach similar to the Minimal API one, while being more performant and most importantly more complete.

Why? Mainly because Microsoft’s Minimal APIs are still new and not fully matured. You will find yourself reinventing the wheel sometimes while there’s already a very mature community project alternative which is FastEndpoints. You get integrated FluentValidation, Source generators, Response caching and a lot of fancy stuff that you will need to write yourself for your API.

Check it out on GitHub, I have personally used it in a lot of projects and got a very productive and clean way to build and API !

Disclaimer

I am not throwing hate on the Mediator Pattern or Jimmy Bogard’ MediatR library. I have used and am using MediatR in 40% of my applications and I absolutely love it.

However, MediatR should be used when needed in a very defined context. I am just saying that if you’re using it only to write ‘Clean Code’, then you are probably better suited with FastEndpoints or a Manual Separation. The library was built to solve other problems and by no means should become an industry standard for writing clean code.