Understanding CQRS Design Pattern

Most architects/developers are encouraged to follow the design patterns when working on larger applications, resulting in more robust, secure, and maintainable applications. One of the widely used patterns is CRQS. Let's see more details about this, and will go through the implementation code.

What is the CRQS pattern?

The word CQRS stands for.

  • C: Command
  • Q: Query
  • R: Responsibility.
  • S: Separation/Segregation.

The CRQS pattern is a software architecture pattern. The pattern principle states that Fetching / Querying and Writing data are separate operations, in technical both fetch and write classes should be maintained separately.

Advantages of CRQS Pattern

  • Offers a significant scalability of read and write operations.
  • The separation of each command or query handler can be optimized by developers based on the requirement separately.
  • These separation principles allow developers to write clean and maintainable code.

Let's see the code walk-through.

Open Visual Studio and create a new project.

Visual Studio

Model Class

I have created one class customer. cs class.

public class customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Mobile { get; set; }
        public string Email { get; set; }
        public string Plan { get; set; }
        public decimal Balance { get; set; }
    }

Customer command class

This class is responsible for passing the data to the command handler, this is not mandatory you pass as args [].

//commands
    public class CustomerCommand
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Mobile { get; set; }
        public string Email { get; set; }
        public DateTime CreatedDate { get; set; }
        public string Plan { get; set; }
        public decimal Balance { get; set; }
    }

CustomerDTO

Responsible for storing the response of the customer, this can be changed based on the requirements, and you can add or remove members.

    public class CustomerDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Mobile { get; set; }
        public string Email { get; set; }
        public DateTime CreatedDate { get; set; }
        public string Plan { get; set; }
        public decimal Balance { get; set; }
    }

ApplicationDBcontext

This class is responsible for maintaining all Entities. This is optional, and you can choose your preferred way to handle the entities.

  1. Db First approach.
  2. Model first approach.
    public class ApplicationDBContext:DbContext
    {
        // Here Im not showing the full implemenation of the application db context. for demp purpose I have added the one dbset.
        public ApplicationDBContext()
        {
        }
       public DbSet<customer> customers { get; set; }

    }

Generic repository pattern

Maintains all generic operations like (GET, Insert, Update, Delete) and interacts with Entities. This is also you can go with your own approach for my own sake I have created this generic repo.

//Generic Reporsitory Interface
    public interface IGenericRepo <Tclass> where Tclass:class
    {
        public Task<Tclass> GetbyId(int Id);
        public Task Insert(Tclass tclass);
    }
    public class GenericRepo<Tclass> : IGenericRepo<Tclass> where Tclass:class
    {
        // use you application DB context. I have added the dummy context.
        ApplicationDBContext dBContext;
        private DbSet<Tclass> table;
        public GenericRepo(ApplicationDBContext context)
        {
            dBContext = context;
            table = dBContext.Set<Tclass>();
        }
        public async Task<Tclass> GetbyId(int Id)
        {
            return  table.Find(Id);
        }
        public async Task Insert(Tclass tclass)
        {
            var result = table.Add(tclass);
        }
    }

ICommandHandler

Create an Interface and define the structure of it. That needs to be followed in the implementation class.

 //Hander Interface
    public interface ICommandHandler <in Tcommand,Tresult> where Tcommand :class
    {
        public Task<Tresult> HandleAsync(Tcommand tcommand);
    }

Implementation of Query Handler

//Customer Handler Implemenation
    public class CustomerQueryHandler : ICommandHandler<CustomerCommand, CustomerDTO>
    {
        private IGenericRepo<customer> customerRepo;
        public CustomerQueryHandler(IGenericRepo<customer> repo)
        {
            customerRepo = repo;
        }
        public async Task<CustomerDTO> HandleAsync(CustomerCommand tcommand)
        {
            //bussiness logic will go here.
            if(tcommand.Id==0)
            {
                throw new MissingFieldException("Pass the vdalid Id");
            }
            var customer = await customerRepo.GetbyId(tcommand.Id);
            return new CustomerDTO() { Id= customer .Id,Balance= customer.Balance,Plan=customer.Plan,Email=customer.Email }; 
       
        }
    }

Implementation of Command Handler

public class CreateCustomerHandler : ICommandHandler<CustomerCommand, CustomerDTO>
    {
        private IGenericRepo<customer> customerRepo;
        public CreateCustomerHandler(IGenericRepo<customer> repo)
        {
            customerRepo = repo;
        }
        public  async Task<CustomerDTO> HandleAsync(CustomerCommand tcommand)
        {
            //bussiness logic will go here.
            if (string.IsNullOrEmpty(tcommand.Email))
            {
                throw new MissingFieldException("Pass the vdalid Email");
            }
            await customerRepo.Insert(new customer() {Email=tcommand.Email,Mobile=tcommand.Mobile,Name=tcommand.Name, });

            return new CustomerDTO() {Id=tcommand.Id,Email=tcommand.Email }; 
        }
    }

   

Calling the Query Handler and Command Handler

 ApplicationDBContext applicationDBContext = new ApplicationDBContext();
            IGenericRepo<customer> repo = new GenericRepo<customer>(applicationDBContext);
            
            //Responsible for the customer queries
            ICommandHandler<CustomerCommand,CustomerDTO> queryHandler=new CustomerQueryHandler(repo);
            Console.WriteLine("Sample demo of CQRS pattren ,query");
            CustomerDTO customer=await queryHandler.HandleAsync(new CustomerCommand() { Id = 1, Name = "Jhon" });
            Console.WriteLine("The customer plan" +customer.Plan);

            //Responsible for the customer queries
            ICommandHandler<CustomerCommand, CustomerDTO> commandHandler = new CustomerQueryHandler(repo);
            Console.WriteLine("Sample demo of CQRS pattren ,command");
            CustomerDTO customerInsert = await commandHandler.HandleAsync(new CustomerCommand() { Name = "Jhon",Email="[email protected]",Mobile="1234567890" });


Similar Articles