This article aims to describe the mediator design pattern and how to facilitating using this pattern in .NET with an example. First, the basic explanations and concepts will be mentioned, then the given explanations will be implemented with an example.
The Mediator pattern is one of the behavioral patterns of Gang of Four (GOF) Design Patterns. The use of this design pattern causes integration and reduction of disorder in the dependency between objects. In this pattern, any direct communication between two objects will be limitted and they force to use an interface object called Mediator to communicate with each other.
Without using the mediator pattern, the complexity of communication between different components can be very high. For example, in the Figure-1, four components must be connected to each other in order to sync, update, or communicate (for any purpose). As can be seen, for four components, the complexity is high. Now imagine increasing the number of these components! What a mess and complexity will arise. The result of these complications leads to the implementation becoming more difficult and increasing the communication overhead.
Figure-1: Communication between different components without Mediator pattern
On the other hand, when we use the mediator pattern, the dependencies and connections become loose and it gives us the ability to apply changes more easily. In the figure below, you can see a schematic view of the using mediator pattern.
Figure-2: Communication between different components with Mediator pattern
Similar to all design patterns, this pattern also has its advantages and disadvantages, which will be discussed further.
- Loose coupling: It causes the reduction of coupling in the code, which reduces the repetition of the code as a result.
- Reusability: Each of the required components can be replaced with other new components without affect other part of the pattern.
- Less communication: If a component wants to communicate with another component, it just needs to send a command to mediator.
- Follow the SRP: Mediator pattern follows the Single Responsibility principle, from the SOLID principles. Due to the reduction of communication between components, the main logic will be placed inside the mediator. Because of this, the components don’t contain logic, which means they only have one reason to change their class.
- Follow the OCP: Mediator pattern follows the Open/Closed principle, from the SOLID principles. We can simply add another Mediator class in our code without making any changes to the components.
- Intimate Mediator Behavior: Because the mediator contains the main logic, it will grow over time and become a crucial factor in your application. This growth might lead to a “God object”.
- Maintability: Because all communication among components is through the mediator, and on the other hand mediator object is a central point to control interactions between components, naturally maintaining an object like the mediator will be a challenge.
NOTE: In this article, we do not intend to implement the mediator pattern. Our intention is to use a tool to facilitate the use of this pattern in .NET. Therefore, you can refer to the Here for additional information. There, in addition to a complete explanation of the mediator pattern, it has also provided its implementation, in different programming languages.
The MediatR library is an open source project that is implementation of the Mediator pattern in .NET that supports Request and Command. In MediatR, each request is placed in a queue in the memory and then connected to the Handler corresponding to that request. MediatR has no dependencies on other libraries and uses generics in C# to create Response and also to determine Handlers. Another very good feature of MediatR is the support of the Pub/Sub template, by which you can create a notification and call this notification in different places of the project.
Here you can see the link to the MediatR project on github.
NOTE: In this article, we don't intend to use the Pub/Sub feature and we will focus mostly on Request and Command.
In this part, a very demonstartion program will be writen to show you how to use MediatR library to implemented mediator pattern in .Net application. Our example is a web API example that lists products and also finds a product based on its id. In this example, it has been tried to implement GetAllProducts and GetProductById operations separately and in two different handlers. Then mediatR will map the requests to the corresponding handlers for us. This project implemented via .NET 6.0 and C# programming language. You can use VSCode, Visual Studio, or any other IDE or text editor.
First, create a web API project. You can do this using Visual Studio or use .NET CLI for it. Then, install the MedaitR and MediatR.Extensions.Microsoft.DependencyInjection libraries. You can do this by using .NET cli with the following commands.
1 2 dotnet add package MediatR dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
or doing this through Visual Studio Package installer by following commands:
1 2 Install-Package MediatR Install-Package MediatR.Extensions.Microsoft.DependencyInjection
After installing the desired package, create two folders called Queries and Services in your project. The structure of your solution should look something like the figure below.
Figure-3: Solution structure
In the Services folder, we create a class called Product and write the following code in it.
The next step is to write a service for the product that has the task of listing products as well aso geting details of product by its Id. For this, we create the interface IProductService and ProductService and write the following codes in them.
The next step we need to do is create the Request and the corresponding Handler for every requests or commands we want to do. In practice, we only call the request. It is MediatR, or any other mediator pattern library, that is responsible for finding and mapping the request with its corresponding handler. Inside the Queries folder, which was created, we add two classes named GetAllProductsQuery and GetProductByIdQuery. Like the Figure-4 as below.
Figure-4: Solution structure with Queries expand folders
GetAllProductsQuery is responsible for returning the list of products and GetProductByIdQuery is responsible for finding the product based on the desired ID. The following codes are related to these two classes.
The next step we need to do is to create an instance of IMediator and inject it into the api controller’s constructor. After that, the created requests can be called in each of the methods and actions in the controller. The following code demonstrates this.
At the end, it is time to register ProductService and MediatR. We add them in ConfigureServices (inside startup.cs or it may be minimal api) as below.
No you can compile and run it. After running, you can test the Api and check the correctness of its operation.
NOTE: There are many tools for API testing. The most famous of them is Postman, which is used by many backend developers. If you are in the development environment, you can use openApi tools such as Swagger for .NET, which in addition to API documentation, you can also run your API. If you are using an IDE like VSCode, the REST Client extension can perform this operation for you. Just note that the REST Client is a scripting environment and does not have a GUI.
For example, as can be seen in the image below, the Swagger tool used to facilitate works with REST-API.
Figure-5: Swagger OpenApi Execution Product Api With MediatR
Here, you can find the source code of this project on Github.
The mediator pattern is one of the patterns that reduce the complexity and dependency between components. In this article, this pattern (mediator pattern) and its advantages and disadvantages were investigated. In the following, the MediatR library was introduced and it was shown how implementing the Mediator pattern using the MediatR library is a very easy and hassle-free task, which, in addition to providing a clean solution, helps a lot to create loose coupling between different components. The given example was implemented within an ASP.NET WebApi project.