CQRS and Mediator Pattern in a .NET 8 Web API

Introduction

Each design pattern addresses issues related to software development. In this article, we will explore how to implement CQRS and Mediator in a. NET 8 Web API.

This article is suitable for beginners, intermediates, and professionals. We will cover,

  1. Introduction of CQRS
  2. Introduction of Mediator
  3. Step by Step implementation of CQRS and Mediator

Let’s start with, What is CQRS

Introduction of CQRS

CQRS stands for

  • C – Command
  • Q – Query
  • R – Responsibility
  • S- Segregation

This architecture pattern separates Read data and Write/Update data operations from each other.

Problem

In the big application, it is usual for applications to carry out numerous read/write operations; however, when the application’s users and the volume of operations increase dramatically, it is probable that the application will encounter problems like Scalability, Performance, Complexity, Flexibility, Consistency, etc.

Let’s see the below image.

Application

Solution

Command Query Responsibility Segregation architecture pattern solves the problem and improves scalability, performance, flexibility, etc.

Query Responsibility

Introduction of Mediator

The mediator design pattern seeks to minimize dependencies among objects by limiting direct interactions and instead establishing a means for them to work together solely through the mediator object. In simpler terms, an object exists that encapsulates and oversees the interactions between other objects.

Problem

Each application should be knowledgeable about the interface of the other applications, which will become challenging in the complex application. In this scenario, objects are closely interconnected with one another, making it difficult to manage the applications.

Problem

Solution

The mediator pattern will address this issue. According to the image below, services can interact with each other via a mediator object rather than communicating directly with one another.

Mediator

Now, we will implement CQRS +Mediator patterns using .NET Core 8.0.

Implementation of CQRS and Mediator

Step 1. Create a WeAbi project called “CQRDDemo”

Step 2. Install the Mediator nuget package.

Install Mediator

Step 3. Configure the “Mediator R” Pattern in the Web API Project.

builder.Services.AddMediatR(config => 
    config.RegisterServicesFromAssembly(typeof(Program).Assembly));

Step 4. Now, we will implement the "Member.cs" file.

public class Member
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
}

Step 5. Create 1. “IDataAccess.cs” and “DataAccess.cs” files.

IDataAccess.cs

public interface IDataAccess
{
    public Task<List<Member>> GetMembers();
    public Task Member AddMember(int id, string name, string address);
}

DataAccess.cs

namespace CQRSDemo.DataAccess
{
    public class DataAccess:IDataAccess
    {
        private List<Member> _member = new();|
        public DataAccess()
        {
            _member.Add(new Member{ ID = 1, Name = "Kirtesh Shah", Address = "Vadodara" }); 
            _member.Add(new Member{ ID = 2, Name = "Nitya Shah", Address = "Mumbai" });
        }
    
        public async Task Member AddMember(int id, string name, string address)
        {
            Member member = new Member { ID= id, Name = name, Address =address };
            _member.Add(member);
           await Task.CompletedTask;
        }
        public async Task<List<Member>> GetMembers()
        {
            return await Task.FromResult(member);
        }
    }
}

Step 6. Register dependencies in the program file.

builder.Services.AddScoped<IDataAccess, DataAccess>();

Step 7. Now we will create GetMemberQuery.cs

using CQRSDemo.DataAccess; 
using MediatR;
namespace CQRSDemo.Query
{
    public record GetMemberQuery(): IRequest<List<Member>>;
}

Step 8. Create the GetMemberHandler class and implement IRequestHandler provided by the Mediator R nuget package.

using CQRSDemo.DataAccess; 
using MediatR;
namespace CQRSDemo.Query
{
    public class GetMemberHandler: IRequestHandler<GetMemberQuery, List<Member>>
    {
        private readonly IDataAccess _dataAccess;
        
        public GetMemberHandler(IDataAccess dataAccess)
        {
        _dataAccess = dataAccess;
        }
    }
    async Task<List<Member>> IRequestHandler<GetMemberQuery, List<Member>>.Handle(GetMemberQuery request, CancellationToken cancellationToken) => await _dataAccess.GetMembers();
}

Step 9. Now we will implement MemberController.cs file.

using CQRSDemo.Query; 
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace CORSDemo. Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class MemberController: ControllerBase
    {
        private readonly IMediator _mediator;
        
        public MemberController (IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet]
        public async Task<IActionResult> GetMembers()
        {
            var request = new GetMemberQuery();
            var response= await _mediator.Send(request); 
            return Ok(response);
        }
    }
}

Step 10. Now, we will execute and verify the output.

Output

Great! The query implementation is done, and we will implement the command now.

Step 11. Create “AddMemberCommand.cs”

using CQRSDemo.DataAccess; 
using MediatR;

namespace CQRSDemo.Command
{
    public record AddMemberCommand(Member Member): IRequest;
}

Step 12. Add "AddMemberHandler.cs" file.

using CQRSDemo.DataAccess; 
using MediatR;
namespace CQRSDemo.Command
{
    public class AddMemberHandler: IRequestHandler<AddMemberCommand>
    {
        private readonly IDataAccess dataAccess;
         public AddMemberHandler(IDataAccess dataAccess)
        {
            _dataAccess dataAccess;
        }
        public async Task Handle (AddMemberCommand request,
        CancellationToken cancellationToken) => await _dataAccess.AddMember(request.id, request.name, request.address);
    }
}

Step 13. Now, make changes in the MemberController. Add HttpPost endpoint to add members in the lisṭ.

[HttpPost]
public async Task<IActionResult> AddMember([FromBody] Member member)
{
    var response =_mediator.Send(new AddMemberCommand(member.ID, member.Name, member.Address)); 
    return Ok(response);
}

Step 14. Execute and call the AddMember endpoint from the swagger.

 AddMember

Wow, so we have implemented CQRS and Mediator patterns using .Net 8.

Hope you enjoyed this article and find it useful.

Up Next
    Ebook Download
    View all
    Learn
    View all