April 29, 2024

Practical implementation of the Mediator design pattern using the MediatR package in .NET

IT Tips & Insights

The mediator design pattern tells us how to handle communication between objects without creating high coupling dependencies.

Today we are going to take a look at how to implement it using the MadiatR package in .NET.

Why use the Mediator pattern? 

This is a Behavior Design Pattern that seeks to solve the issue of chaotic dependencies between objects. It encapsulates how a set of objects interact, promoting loose coupling, as no object will know how to create an instance of another and refer to it directly, helping us to follow the Single Responsibility Principle. The only instance that will know these rules is the Mediator class, in our case MediatR will do this job.

Scenario

Let’s say in this example that we are developing a banking system that on user creation, after we create the new user on the database, we need to notify the user via email and start an internal process to define his credit limit.

Hands On

The service responsible for it would look something like this:

We would have a Controller receiving the create request and needing to create, or in this case, receive by injection a UserService and calling the create method (directly referring to the wanted method).

The same thing would happen on the Service, where it would need to know who to call after it’s job it’s finished.

Now let’s refactor this code and let MediatR handle the communication between these classes for us:

First we’re gonna add the IRequest interface and create a new class that implements the IRequestHandler interface. This is the way the mediator knows who to call whenever it is required.

Then we’ll remove the reference to our IUserService in the controller and add the reference to our mediator. It should look like this:

And our handler will look something like this:

Now every time somewhere in our code base, we create an instance of the UserRequest class and pass it to our mediator in the Send method, it will route our logic to our CreateUserRequestHandler class and we don’t need to know how to instantiate it.

But there’s still one part of the problem we didn’t solve. We still need to notify the user and start the credit validation and for this we’ll use MediatR’s notification feature.

Again, we’ll create a new class that now implements the INotification interface from MediatR, like this:

Then we’ll create NotificationHandlers for all the other steps we need to accomplish:

Like this, the service that creates the user doesn’t need to know what happens after it finishes it’s job or who it needs to call, so if we need to change anything to this process in the future, like sending a push notification for example, we’ll only add a new NotificationHandler and our mediator will handle the rest, so we can just create a new instance of our UserCreatedNotification and publish to the mediator like this:

There you go, now we have a method to create a user that only depends on the mediator and the database, easy to maintain, test and to expand if needed in the future.

I’m Lucas Negrini. I live in Curitiba, Brazil and I’m a back-end developer and have worked with .Net for more than 4 years.
I am currently working as a Fullstack .Net/Angular developer at Softensity, constantly learning from the team and the challenging projects I find myself collaborating on.
Thank you for your time to read this entry, I will be open to any comments or questions.

.NET,
BACK TO MAIN PAGE

Let’s Talk