Ответ
CQRS (Command Query Responsibility Segregation) — это архитектурный паттерн, который разделяет модели для операций чтения (Query) и записи (Command). Основная цель — независимая оптимизация, масштабирование и обеспечение безопасности для каждого типа нагрузки.
Основные концепции
- Команда (Command): Изменяет состояние системы (например,
CreateOrder,UpdateUserEmail). Команда не возвращает бизнес-данные, только подтверждение (успех/ошибка). - Запрос (Query): Возвращает данные, не изменяя состояние системы (например,
GetUserProfile,GetOrderHistory).
Преимущества
- Разделение ответственности: Упрощает код, так как модели чтения и записи решают разные задачи.
- Независимая оптимизация: Можно использовать разные базы данных (например, SQL для команд и оптимизированное NoSQL/кэш для запросов).
- Повышенная безопасность: Легче применять разные правила авторизации для операций изменения и просмотра данных.
- Масштабируемость: Чтение и запись можно масштабировать независимо друг от друга.
Пример реализации на C
// Команда
public record CreateProductCommand(string Name, decimal Price);
public class ProductCommandHandler
{
private readonly WriteDbContext _dbContext;
public ProductCommandHandler(WriteDbContext dbContext) => _dbContext = dbContext;
public async Task Handle(CreateProductCommand command)
{
var product = new Product { Name = command.Name, Price = command.Price };
_dbContext.Products.Add(product);
await _dbContext.SaveChangesAsync();
}
}
// Запрос
public record GetProductQuery(int ProductId);
public class ProductQueryHandler
{
private readonly ReadDbContext _dbContext;
public ProductQueryHandler(ReadDbContext dbContext) => _dbContext = dbContext;
public async Task<ProductView> Handle(GetProductQuery query)
{
// Используется упрощенная модель, оптимизированная для чтения
return await _dbContext.ProductViews
.FirstOrDefaultAsync(p => p.Id == query.ProductId);
}
}
Когда использовать CQRS?
Паттерн оправдан в системах со сложной бизнес-логикой, высокими нагрузками, где требования к чтению и записи сильно различаются. Для простых CRUD-приложений он может излишне усложнить архитектуру.