Implementing the CQRS (Command Query Responsibility Segregation) pattern in ASP.NET Core Web API involves separating read and write operations. It's a structural pattern that can help in scaling and maintaining applications. Here, I'll guide you through the steps for implementing CQRS in an ASP.NET Core Web API.
For brevity, I'll provide a high-level overview with code snippets. You'll need to adapt and extend these examples to suit your specific needs and structure.
Create a Solution and Projects
Create an ASP.NET Core Web API project and two separate projects for commands and queries.
Create a Model For Product
namespace MicroservicesWithCQRSDesignPattern.Model
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Create a Model for Query with data Filters
namespace MicroservicesWithCQRSDesignPattern.Model
{
public class GetProductsQuery
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public string SearchTerm { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
}
}
Command and Query Models for Create Product
Create models for commands and queries. For instance.
namespace MicroservicesWithCQRSDesignPattern.Quries.CommandModel
{
public class CreateProductCommand
{
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Command and Query Models for Delete Product
namespace MicroservicesWithCQRSDesignPattern.Quries.QueryModel
{
public class DeleteProductCommand
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Command and Query Models for Update Product
namespace MicroservicesWithCQRSDesignPattern.Quries.QueryModel
{
public class UpdateProductCommand
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Command and Query Models for Get All Product
namespace MicroservicesWithCQRSDesignPattern.Quries.QueryModel
{
public class GetAllProductCommand
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Create Interface for ICommandHandler<TCommand>
namespace MicroservicesWithCQRSDesignPattern.Interfaces
{
public interface ICommandHandler<TCommand>
{
Task Handle(TCommand command);
}
}
Create Interface for IQueryHandler<TQuery, TResult>
namespace MicroservicesWithCQRSDesignPattern.Interfaces
{
public interface IQueryHandler<TQuery, TResult>
{
Task<TResult> Handle(TQuery query);
}
}
Create an Interface for IRepository
namespace MicroservicesWithCQRSDesignPattern.Interfaces
{
public interface IRepository<T>
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
Task SaveAsync();
}
}
Implement the Repository Pattern for Products
using MicroservicesWithCQRSDesignPattern.AppDbContext;
using MicroservicesWithCQRSDesignPattern.Interfaces;
using MicroservicesWithCQRSDesignPattern.Model;
using Microsoft.EntityFrameworkCore;
namespace MicroservicesWithCQRSDesignPattern.Repository
{
public class ProductRepository : IRepository<Product>
{
private readonly ApplicationDbContext _dbContext;
public ProductRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Product> GetByIdAsync(int id)
{
return await _dbContext.Set<Product>().FindAsync(id);
}
public async Task<IEnumerable<Product>> GetAllAsync()
{
return await _dbContext.Set<Product>().ToListAsync();
}
public async Task AddAsync(Product entity)
{
await _dbContext.Set<Product>().AddAsync(entity);
}
public async Task UpdateAsync(Product entity)
{
_dbContext.Set<Product>().Update(entity);
}
public async Task DeleteAsync(Product entity)
{
_dbContext.Set<Product>().Remove(entity);
}
public async Task SaveAsync()
{
await _dbContext.SaveChangesAsync();
}
}
}
Create Handlers for Create, Update, Delete, GetAllProducts
CreateProductCommandHandler
using MicroservicesWithCQRSDesignPattern.Interfaces;
using MicroservicesWithCQRSDesignPattern.Model;
using MicroservicesWithCQRSDesignPattern.Quries.CommandModel;
namespace MicroservicesWithCQRSDesignPattern.Handlers
{
public class CreateProductCommandHandler : ICommandHandler<CreateProductCommand>
{
private readonly IRepository<Product> _repository;
public CreateProductCommandHandler(IRepository<Product> repository)
{
_repository = repository;
}
public async Task Handle(CreateProductCommand command)
{
var product = new Product
{
Name = command.Name,
Price = command.Price
};
await _repository.AddAsync(product);
await _repository.SaveAsync();
}
}
}
DeleteProductCommandHandler
using MicroservicesWithCQRSDesignPattern.Interfaces;
using MicroservicesWithCQRSDesignPattern.Quries.CommandModel;
using MicroservicesWithCQRSDesignPattern.Model;
using MicroservicesWithCQRSDesignPattern.Quries.QueryModel;
namespace MicroservicesWithCQRSDesignPattern.Handlers
{
public class DeleteProductCommandHandler : ICommandHandler<DeleteProductCommand>
{
private readonly IRepository<Product> _repository;
public DeleteProductCommandHandler(IRepository<Product> repository)
{
_repository = repository;
}
public async Task Handle(DeleteProductCommand command)
{
var productToDelete = await _repository.GetByIdAsync(command.Id);
if(productToDelete != null)
{
await _repository.DeleteAsync(productToDelete);
}
else
{
throw new Exception("Product not found"); // Handle product not found scenario
}
}
}
}
GetProductsQueryHandler
using MicroservicesWithCQRSDesignPattern.Interfaces;
using MicroservicesWithCQRSDesignPattern.Model;
using MicroservicesWithCQRSDesignPattern.Quries.QueryModel;
namespace MicroservicesWithCQRSDesignPattern.Handlers
{
public class GetProductsQueryHandler : IQueryHandler<GetProductsQuery, IEnumerable<GetAllProductCommand>>
{
private readonly IRepository<Product> _repository; // Inject repository or database context
public GetProductsQueryHandler(IRepository<Product> repository)
{
_repository = repository;
}
public async Task<IEnumerable<GetAllProductCommand>> Handle(GetProductsQuery query)
{
var products = await _repository.GetAllAsync(); // Implement repository method
// Map products to ProductViewModel
return products.Select(p => new GetAllProductCommand
{
Id = p.Id,
Name = p.Name,
Price = p.Price
});
}
}
}


Join the conversation! Your thoughts help the community grow.