IT Tips & Insights: A step-by-step guide to implement the Mediator Design Pattern in order to create a function that’s easy to maintain, test and expand.Â
By Lucas Negrini, Software Engineer
The mediator design pattern tells us how to handle communication between objects without creating high coupling dependencies. Today, we’re 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. This helps us to follow the Single Responsibility Principle. The only instance that will know these rules is the Mediator class, and in our case, MediatR will do this job.
Scenario
In this example, let’s say that we’re developing a banking system and need to create a new user. After we create the new user in 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 making this happen would look something like this:
We would have a controller receive the create request. To create the necessary UserService instance, we could either instantiate it directly or inject it into the controller. Then, we would call the UserService’s create method, referencing it directly to ensure the correct method is invoked.
Similarly, on the Service side, once its task is complete, it would also need to know which component to invoke next.
Now let’s refactor this code and let MediatR handle the communication between these classes for us:
First we’re going to 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 we create an instance of the UserRequest class somewhere in our code base 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. 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:
The service that creates the user doesn’t need to know what happens after it finishes its job, or who it needs to call. So if we need to change anything in this process in the future, (ex: sending a push notification), we only have to add a new NotificationHandler and our mediator will handle the rest. 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, that’s easy to maintain, test and expand if necessary in the future.
About
Hi, I’m Lucas Negrini from Curitiba, Brazil. I’m a back-end developer that has worked with .NET for more than 4 years. I currently work as a Fullstack .NET/Angular developer at Softensity, constantly learning from the team and the challenging projects I find myself collaborating on. Thanks for taking the time to read this entry. I’m open to any comments or questions.