May be for some of you, The Mediator pattern is known but here my focus is to using the Mediator pattern with CQRS (Command and Query Responsibility Segregation) pattern to make our .net core (API or web) code cleaner, extensible and maintainable.
We often see, In complex project when the dependencies are increased then our controller gets overloaded and tightly coupled with dependent services while using the built-in IoC container provided by ASP.NET Core.
Take a scenario example as Loan Application approval process. In this case, loan approval has to go through various process such as:
1. CIBIL Score checks: ; 2. Employment Status check: ; 3. Account Details validation: ; 4. Payment History: ; 5. EMI to Income Ratio check etc.
And assume in this case, all five processes are as different services and we have a web api endpoint loan application approval process to perform the loan approval. Our code will look like this:
[ApiController]
[Route("[controller]")]
public class LoanProcessController : ControllerBase
{
private ICibilService _cibilService;
private IEmploymentService _employmentService;
private IAccountService _employmentService;
private IPaymentService _paymentService;
private IEMIService _emiService;public LoanProcessController(ICibilService cibilService,
IEmploymentService employmentService,
IAccountService accountService,
IPaymentService paymentService,
IEMIService emiService)
{
_cibilService = cibilService;
_employmentService = employmentService;
_accountService = accountService;
_paymentService = paymentService;
_emiService = emiService;
}[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<LoanApplication>> ApproveLoan()
{
_cibilService.ValidateCibilScore();
_employmentService.ValidateEmployeeStatus();
_accountService.ValidateAccount();
_paymentService.ValidatePaymentHistory();
__emiService.ValidatePaymentEMIPaymentRatio();
}
}
This is where we need a mediator to decouple the multiple services dependencies on controller. Hence the above code must be refactored using mediator pattern and it should be like as:
Seems easy going in implementation but this become more easy to implement with MediatR Extensions for Microsoft Dependency Injection with CQRS pattern.
Pardon me as I’m going to show you a simple example instead of creating a big example to depict the above scenario. Here I’m going to tweak the weather forecast sample application to demonstrate the mediator pattern with CQRS.
To follow me with this example, create a web api project using the web api template in .Net 6. This template will create weather forecast api for you and then lets follow the below steps to implement MediatR with CQRS to retrieve the weather forecast.
Step 1: Install the MediatR nugets.
Step 2: Create the CQRS folder structure to separate our your commands and queries related classes.
Command folder is for commands related to state change operations like create, update, delete etc.
and the Query folder is for queries requests like read which in general doesn’t change the state of data.
“Based on our above loan approval, probably CQRS pattern would be overthought pattern hence can be avoided simply and steak to mediator pattern only.
Step 3: Register the mediatR service
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());
Step 4: Create query request and handler
using MediatR;namespace CQRSWebAPIDemo.CQRS.Query
{
public class GetWeatherForecastQueryRequest : IRequest<IEnumerable<WeatherForecast>>
{
private static readonly string[] Summaries = new[]{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public DateTime DateTime { get; set; }public class GetWeatherForecastQueryHandler : IRequestHandler<GetWeatherForecastQueryRequest, IEnumerable<WeatherForecast>>
{
private ILogger<GetWeatherForecastQueryHandler> _logger;public GetWeatherForecastQueryHandler(ILogger<GetWeatherForecastQueryHandler> logger)
{
_logger = logger;
}public async Task<IEnumerable<WeatherForecast>> Handle(GetWeatherForecastQueryRequest query, CancellationToken cancellationToken)
{
var dateTime = query.DateTime;
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = dateTime.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
}
}
}
}
Syntax for creating a Query or command request is as:
public class {Command_Query_request_name} : IRequest<{response_type}>
you should define all your request parameter here, for an example purpose I have added DateTime for request as input.
similarly the syntax for command handler is as:
public class {handler_Name} : IRequestHandler<{Command_Query_request_name type}, {response_type}>
Step 5: Go to WeatherForecast controller and modify the code to call through mediator command.
using CQRSWebAPIDemo.CQRS.Query;
using MediatR;
using Microsoft.AspNetCore.Mvc;namespace CQRSWebAPIDemo.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
private IMediator _mediator;public WeatherForecastController(IMediator mediator, ILogger<WeatherForecastController> logger)
{
_logger = logger;
_mediator = mediator;
}[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<WeatherForecast>> Get()
{
return await _mediator.Send(new GetWeatherForecastQueryRequest { DateTime = DateTime.Now });
}
}
}
Step 6: We are done, You can now run and validate.
Conclusion:
The mediator pattern with CQRS structure is a great pattern to reduce the dependencies and make code clean within your application which helps you to reuse your components and also to keep you in line with the Single Responsible Principle.
You can also use the Command process pipeline using Mediator pattern to invoke a command handler. Or you can use messaging service as part of pipeline too for microservice api architecture design.
Hope you like it, Don’t forget to like/comment and follow me to get more like this. Thank You.
No comments:
Post a Comment