1. Giới thiệu về Clean Architecture
Clean Architecture là một mô hình kiến trúc phần mềm được đề xuất bởi Robert C. Martin (Uncle Bob), nhằm mục tiêu xây dựng các hệ thống có tính module cao, dễ mở rộng, bảo trì và kiểm thử. Kiến trúc này giúp phần mềm tránh sự phụ thuộc lẫn nhau giữa các thành phần, từ đó giảm thiểu sự thay đổi ảnh hưởng đến toàn bộ hệ thống.
2. Cấu trúc Clean Architecture
Clean Architecture chia hệ thống thành nhiều vòng tròn đồng tâm, với nguyên tắc quan trọng là các vòng trong không được phụ thuộc vào các vòng ngoài. Các thành phần chính của mô hình này bao gồm:
a. Entities (Tầng Core Domain)
Entities là các đối tượng trung tâm của hệ thống, đại diện cho các quy tắc nghiệp vụ cốt lõi và không phụ thuộc vào bất kỳ thành phần nào bên ngoài.
Ví dụ về Entity:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
b. Use Cases (Tầng Application Logic)
Tầng này chứa các quy tắc nghiệp vụ cụ thể của ứng dụng, xác định cách dữ liệu được xử lý và luân chuyển giữa các tầng.
c. Interface Adapters (Tầng Presentation & API)
Tầng này chịu trách nhiệm chuyển đổi dữ liệu giữa các tầng bên trong và bên ngoài. Nó bao gồm các thành phần như API, UI Controllers, View Models, hoặc DTOs để giao tiếp với các tầng trên.
d. Infrastructure & External Interfaces (Tầng Framework & Tools)
Tầng ngoài cùng chứa các thành phần phụ thuộc như cơ sở dữ liệu, framework, thư viện và giao diện người dùng.
3. Nguyên tắc chính của Clean Architecture
- Dependency Rule (Nguyên tắc phụ thuộc): Các vòng trong không được phụ thuộc vào các vòng ngoài.
- Separation of Concerns (Phân tách mối quan tâm): Mỗi tầng có nhiệm vụ riêng biệt, không ảnh hưởng lẫn nhau.
- Testability (Dễ kiểm thử): Do các tầng độc lập, ta có thể dễ dàng viết unit test mà không cần quan tâm đến tầng khác.
4. CQRS trong .NET Core
a. CQRS là gì?
CQRS (Command Query Responsibility Segregation) là một mô hình kiến trúc giúp tách biệt các thao tác ghi (Command) và đọc (Query) trong hệ thống.
b. Cấu trúc CQRS
- Command: Chịu trách nhiệm thực hiện các hành động thay đổi trạng thái hệ thống (Create, Update, Delete).
- Query: Chỉ dùng để truy vấn dữ liệu, không thay đổi trạng thái hệ thống.
c. Ứng dụng CQRS trong .NET Core với Clean Architecture
Cấu trúc thư mục khi kết hợp CQRS với Clean Architecture:
📂 src/
┣ 📂 Core/ # Entities và Use Cases
┃ ┣ 📂 Entities/
┃ ┣ 📂 UseCases/
┃ ┣ 📂 Commands/ # Các lệnh cập nhật dữ liệu
┃ ┣ 📂 Queries/ # Các lệnh truy vấn dữ liệu
┣ 📂 Application/ # Service Interfaces và DTOs
┣ 📂 Infrastructure/ # Repository, Database, Frameworks
┣ 📂 Presentation/ # Controllers, Views, API
┣ 📂 Tests/ # Unit Tests, Integration Tests
d. Triển khai Command & Query trong .NET Core
Command – Thêm Người Dùng
public class CreateUserCommand : IRequest<int>
{
public string Name { get; set; }
public string Email { get; set; }
}
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
private readonly IUserRepository _userRepository;
public CreateUserCommandHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var user = new User { Name = request.Name, Email = request.Email };
return await _userRepository.AddUserAsync(user);
}
}
Command – Cập nhật thông tin người dùng
public class UpdateUserCommand : IRequest<bool>
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UpdateUserCommandHandler : IRequestHandler<UpdateUserCommand, bool>
{
private readonly IUserRepository _userRepository;
public UpdateUserCommandHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task<bool> Handle(UpdateUserCommand request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetUserByIdAsync(request.Id);
if (user == null) return false;
user.Name = request.Name;
user.Email = request.Email;
return await _userRepository.UpdateUserAsync(user);
}
}
Query – Lấy danh sách người dùng
public class GetUsersQuery : IRequest<List<User>> {}
public class GetUsersQueryHandler : IRequestHandler<GetUsersQuery, List<User>>
{
private readonly IUserRepository _userRepository;
public GetUsersQueryHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task<List<User>> Handle(GetUsersQuery request, CancellationToken cancellationToken)
{
return await _userRepository.GetUsersAsync();
}
}
5. Presentation Layer – API Controller
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserCommand command)
{
var userId = await _mediator.Send(command);
return Ok(userId);
}
[HttpGet]
public async Task<IActionResult> GetUsers()
{
var users = await _mediator.Send(new GetUsersQuery());
return Ok(users);
}
}
6. Kết luận
Việc kết hợp Clean Architecture và CQRS trong .NET Core giúp hệ thống có kiến trúc rõ ràng, dễ mở rộng và bảo trì. Clean Architecture đảm bảo tính module và khả năng kiểm thử, trong khi CQRS giúp tối ưu hóa hiệu suất và phân tách trách nhiệm hợp lý.
Nếu bạn muốn xây dựng các ứng dụng bền vững, dễ bảo trì và có khả năng mở rộng tốt, việc áp dụng cả hai mô hình này là một giải pháp mạnh mẽ!