Triển khai trong .NET Core với MediatR - CQRS Pattern
CQRS là một mẫu thiết kế giúp phân tách trách nhiệm xử lý các lệnh và truy vấn vào các thành phần khác nhau. Mẫu kiến trúc CQRS tập trung chủ yếu vào việc tách biệt cách đọc và ghi dữ liệu. Nó chia thành hai mô hình riêng biệt cho việc đọc và cập nhật dữ liệu trong cơ sở dữ liệu: Truy vấn (Queries) và Lệnh (Commands).
Trong khi hệ thống phần mềm phát triển, việc quản lý dữ liệu ngày càng phức tạp trở nên thách thức. Trong các tình huống như vậy, các phương pháp truyền thống trong thiết kế cơ sở dữ liệu, giả định rằng có một mô hình duy nhất để xử lý cả đọc và ghi, có thể không đủ.
Triển khai CQRS trong ứng dụng của chúng ta có thể tối ưu hiệu suất, khả năng mở rộng và bảo mật của nó.
I. Kiến trúc cơ bản của mẫu CQRS:
1. Command
Command (lệnh) là các chỉ thị cho thấy một sự thay đổi mong muốn trong trạng thái của một thực thể. Những lệnh này thực hiện các hoạt động như Thêm, Cập nhật và Xóa. Chúng không trả về dữ liệu, mà thay vào đó, chúng thay đổi trạng thái của ứng dụng. Mỗi command là một đối tượng chứa tên của hoạt động cùng với dữ liệu cần thiết để thực hiện hoạt động đó. CommandHandlers giải thích các lệnh này và trả về một sự kiện. Sự kiện này có thể là một sự kiện thành công hoặc một sự kiện thất bại tùy thuộc vào kết quả của lệnh. Nếu lệnh thành công, một sự kiện thành công được tạo ra; tuy nhiên, nếu lệnh thất bại, một sự kiện thất bại được tạo ra.
2. Query
Query (truy vấn) được sử dụng để truy xuất thông tin từ cơ sở dữ liệu. Đối tượng truy vấn chỉ trả về dữ liệu và không thực hiện bất kỳ sửa đổi nào vào nó. Truy vấn chỉ bao gồm các phương thức truy xuất dữ liệu. Chúng được sử dụng để đọc dữ liệu từ cơ sở dữ liệu và trả về cho khách hàng để hiển thị trong giao diện người dùng. QueryHandlers giải thích các truy vấn này và trả về giá trị truy vấn.
CQRS được áp dụng trong các tình huống khi việc sử dụng một cơ sở dữ liệu và mô hình duy nhất để xử lý cả đọc và ghi là không hiệu quả. Các trang web thương mại điện tử, hệ thống tài chính và phân tích thời gian thực là các ví dụ về ứng dụng yêu cầu khả năng mở rộng lớn, hiệu suất cao và phức tạp về dữ liệu.
Một trong những lợi ích chính của việc sử dụng CQRS là nó giúp đơn giản hóa thiết kế của các hệ thống phức tạp bằng cách phân tách các loại lệnh và truy vấn. Sự tách biệt này cho phép các nhà phát triển tối ưu hóa mỗi mô hình cho các trách nhiệm và yêu cầu cụ thể của nó. Mô hình lệnh có thể được tối ưu hóa cho hiệu suất ghi cao và tính toàn vẹn dữ liệu, trong khi mô hình truy vấn có thể được tối ưu hóa cho hiệu suất đọc cao và truy cập nhanh chóng vào dữ liệu.
Ví dụ: Trang web thương mại điện tử Tất cả thông tin về sản phẩm sẽ được lưu trữ trong một bảng duy nhất trong thiết kế cơ sở dữ liệu truyền thống, và bảng này sẽ được sử dụng để xử lý cả đọc và ghi. CQRS, tuy nhiên, có hai mô hình riêng biệt: một cho các lệnh và một cho các truy vấn. Mô hình lệnh chịu trách nhiệm quản lý các yêu cầu ảnh hưởng đến trạng thái của chương trình. Trong ví dụ của chúng tôi, mô hình này sẽ quản lý các yêu cầu để thêm, loại bỏ hoặc chỉnh sửa sản phẩm. Mô hình lệnh được thiết kế để đảm bảo tính toàn vẹn dữ liệu trong khi tối đa hóa hiệu suất ghi. Ngược lại, mô hình truy vấn chịu trách nhiệm xử lý các yêu cầu đọc trạng thái ứng dụng. Trong ví dụ của chúng tôi, mô hình này sẽ quản lý các yêu cầu để truy xuất thông tin sản phẩm. Khi một khách hàng tìm kiếm một sản phẩm trên trang web, mô hình tr
II. Triển khai CQRS với MediatR trong .NET CORE
1. Cài đặt MediatR
Cài đặt thư viện MediatR thông qua NuGet Package Manager Console hoặc Quản lý gói NuGet của Visual Studio bằng cách chạy lệnh sau:
Install-Package MediatR
2. Định nghĩa các lệnh (commands) và truy vấn (queries)
// Lệnh thêm sản phẩm
public class AddProductCommand : IRequest
{
public string Name { get; set; }
public decimal Price { get; set; }
}
// Truy vấn lấy thông tin sản phẩm
public class GetProductQuery : IRequest<Product>
{
public int ProductId { get; set; }
}
3. Triển khai các xử lý lệnh và truy vấn
public class AddProductCommandHandler : IRequestHandler<AddProductCommand>
{
private readonly IProductRepository _productRepository;
public AddProductCommandHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<Unit> Handle(AddProductCommand request, CancellationToken cancellationToken)
{
var product = new Product
{
Name = request.Name,
Price = request.Price
};
await _productRepository.AddAsync(product);
return Unit.Value;
}
}
public class GetProductQueryHandler : IRequestHandler<GetProductQuery, Product>
{
private readonly IProductRepository _productRepository;
public GetProductQueryHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<Product> Handle(GetProductQuery request, CancellationToken cancellationToken)
{
return await _productRepository.GetByIdAsync(request.ProductId);
}
}
4. Đăng ký các dịch vụ
services.AddMediatR(typeof(Startup)); // Đăng ký tất cả các xử lý trong dự án
5. Sử dụng MediatR trong ứng dụng
Trong lớp controller hoặc service, bạn có thể sử dụng MediatR để gửi các lệnh và truy vấn.
public class ProductController : ControllerBase
{
private readonly IMediator _mediator;
public ProductController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> AddProduct(AddProductCommand command)
{
await _mediator.Send(command);
return Ok();
}
[HttpGet("{productId}")]
public async Task<IActionResult> GetProduct(int productId)
{
var query = new GetProductQuery { ProductId = productId };
var product = await _mediator.Send(query);
return Ok(product);
}
}
Đây là cách cơ bản để triển khai mẫu thiết kế CQRS bằng thư viện MediatR trong C#. Bạn cũng cần xây dựng các lớp repository, model và các dịch vụ phụ thuộc vào nhu cầu cụ thể của ứng dụng.
Lời kết
Cuối cùng, nhân dịp năm mới, mình chúc toàn tập thể Viblo và mọi người dùng trên nền tảng Viblo một năm mới Vạn sự như ý, tỉ sự như mơ, trăm sự bất ngờ, ngập tràn hạnh phúc!!
All rights reserved