Что такое CQRS?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
CQRS (Command Query Responsibility Segregation)
CQRS — это архитектурный паттерн, разделяющий логику чтения (Query) и записи (Command) данных в отдельные компоненты. Это мощный подход для построения высокопроизводительных и масштабируемых систем.
Основная идея
Вместо того чтобы использовать одну модель данных для операций чтения и записи, CQRS предлагает:
Традиционный подход:
┌─────────────────────┐
│ Application │
└──────────┬──────────┘
│
┌────▼────┐
│Database │
└──────────┘
CQRS подход:
┌─────────────────────┬──────────────────┐
│ Commands (Write) │ Queries (Read) │
└──────┬──────────────┴────────┬─────────┘
│ │
┌──────▼─────────┐ ┌─────────▼────┐
│Write Database │ │ Read Database │
└────────────────┘ │(Cache/View) │
└────────────────┘
Компоненты CQRS
1. Commands (Команды) — операции, которые изменяют состояние
// Команда для создания заказа
public class CreateOrderCommand {
private String customerId;
private List<OrderItem> items;
private BigDecimal totalAmount;
// constructor, getters
}
// Command Handler обрабатывает команду
@Service
public class CreateOrderCommandHandler {
public void handle(CreateOrderCommand cmd) {
Order order = new Order(
cmd.getCustomerId(),
cmd.getItems(),
cmd.getTotalAmount()
);
orderRepository.save(order);
// Опубликовать событие OrderCreatedEvent
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
}
}
2. Queries (Запросы) — операции, которые только читают данные
// Запрос для получения заказов
public class GetCustomerOrdersQuery {
private String customerId;
// constructor, getters
}
// Query Handler обрабатывает запрос
@Service
public class GetCustomerOrdersQueryHandler {
private final OrderReadRepository orderReadRepository;
public List<OrderDTO> handle(GetCustomerOrdersQuery query) {
// Запрос из оптимизированного read store
return orderReadRepository.findByCustomerId(query.getCustomerId());
}
}
Различия между Write и Read моделями
| Аспект | Write Model | Read Model |
|---|---|---|
| Цель | Гарантировать консистентность | Быстро предоставить данные |
| Структура | Нормализованная (3NF) | Денормализованная |
| Обновления | Синхронные, транзакционные | Асинхронные через события |
| Оптимизация | Для вставок/обновлений | Для чтения |
| Примеры БД | PostgreSQL, MySQL | Redis, Elasticsearch, MongoDB |
Процесс работы CQRS с Event Sourcing
1. Пользователь выполняет команду: "Создать заказ"
2. Command Handler обрабатывает команду
3. Генерируется событие: "OrderCreatedEvent"
4. Событие сохраняется в Event Store (source of truth)
5. Event Handler подписывается на событие
6. Read Model обновляется асинхронно (денормализованный вид)
7. Пользователь читает из Read Model (быстро!)
Пример на Java с Spring
// Event - неизменяемое событие
public class OrderCreatedEvent {
private final String orderId;
private final String customerId;
private final BigDecimal amount;
private final LocalDateTime createdAt;
public OrderCreatedEvent(String orderId, String customerId,
BigDecimal amount, LocalDateTime createdAt) {
this.orderId = orderId;
this.customerId = customerId;
this.amount = amount;
this.createdAt = createdAt;
}
}
// Event Publisher
@Component
public class EventPublisher {
private final ApplicationEventPublisher publisher;
public void publish(OrderCreatedEvent event) {
publisher.publishEvent(event);
}
}
// Event Handler - обновляет Read Model
@Component
public class OrderEventHandler {
private final OrderReadRepository readRepository;
@EventListener
public void on(OrderCreatedEvent event) {
OrderReadModel readModel = new OrderReadModel(
event.getOrderId(),
event.getCustomerId(),
event.getAmount(),
event.getCreatedAt()
);
readRepository.save(readModel);
}
}
Преимущества CQRS
- Независимая масштабируемость - read сервис отдельно от write сервиса
- Производительность - оптимизация под специфику (write ≠ read)
- Простота запросов - денормализованные данные
- Аудит и история - все события хранятся (Event Sourcing)
- Масштабируемость read операций - можно добавить несколько read replicas
Недостатки CQRS
- Сложность - больше кода и компонентов
- Eventually consistent - временная несогласованность данных
- Необходимость синхронизации - между write и read моделями
- Оверхед - не нужен для простых приложений
Когда использовать CQRS
✓ Высоконагруженные системы с разными паттернами читать/писать ✓ Комплексные бизнес-правила ✓ Нужна история операций (Event Sourcing) ✓ Read-heavy приложения (аналитика, отчеты) ✓ Микросервисная архитектура
✗ Простые CRUD приложения ✗ Когда нужна strong consistency ✗ Маленькая команда без опыта
Заключение
CQRS — это не серебряная пуля, но мощный паттерн для сложных систем. Я использую его в проектах, где явно видна разница между паттернами чтения и записи, или когда нужна высокая масштабируемость.