cqrs
CQRS (Command Query Responsibility Segregation — разделение ответственности команд и запросов) — это архитектурный паттерн, при котором операции изменения данных (команды) отделены от операций чтения (запросы) как на уровне кода, так и, при необходимости, на уровне модели данных и инфраструктуры. Вместо единой модели и репозитория, обслуживающих и чтение, и запись, создаются отдельные модели для каждого типа операций.
Исторический контекст: CQS
CQRS опирается на принцип CQS (Command Query Separation), сформулированный Бертраном Мейером: каждый метод должен быть либо командой (изменяет состояние, не возвращает данные), либо запросом (возвращает данные, не изменяет состояние) — но не тем и другим одновременно.
CQRS — это применение CQS на уровне архитектуры системы, а не отдельных методов.
Как работает CQRS
В стандартном CRUD-подходе один и тот же объект домена используется для чтения и записи. Метод GetOrder и метод CreateOrder работают с одной моделью.
В CQRS:
- Command side: принимает команды (CreateOrderCommand, UpdateOrderStatusCommand), валидирует их, применяет бизнес-логику через доменные объекты, изменяет состояние (запись в базу). Возвращает только результат операции — успех или ошибку.
- Query side: обрабатывает запросы на чтение, возвращает специализированные DTO, оптимизированные для конкретного представления. Не содержит бизнес-логики изменений.
Зачем разделять модели чтения и записи
Модель для записи оптимизирована для инвариантов бизнес-логики — агрегаты из DDD, нормализованные таблицы, транзакции. Она может быть сложной, но правильно моделирует домен.
Запросы часто требуют денормализованных, агрегированных данных из нескольких источников. Строить сложные JOIN-запросы поверх нормализованной write-модели — дорого и медленно. Read-модель может быть отдельной денормализованной таблицей, документом в MongoDB, проекцией в Elasticsearch — оптимизированной именно для конкретного запроса.
CQRS с синхронной и асинхронной синхронизацией
Синхронный CQRS: команда изменяет write-хранилище и сразу обновляет read-хранилище в той же транзакции или в ответе. Простая реализация, но read и write связаны.
Асинхронный CQRS: команда изменяет write-хранилище и публикует событие. Read-сторона подписана на события и асинхронно обновляет свои проекции. Это вводит eventual consistency: между командой и видимостью изменения в read-стороне есть задержка. Зато read и write масштабируются независимо.
CQRS и Event Sourcing
CQRS часто используется совместно с Event Sourcing, хотя это самостоятельные паттерны. При Event Sourcing write-сторона хранит поток событий; read-сторона строит проекции из этих событий. CQRS обеспечивает разделение путей чтения и записи; Event Sourcing определяет, как хранится write-состояние.
Преимущества CQRS
- Независимое масштабирование read и write: если чтений в 10 раз больше, читательные сервисы масштабируются отдельно.
- Оптимизированные модели: каждая сторона оптимальна для своей задачи.
- Упрощение сложных доменов: write-модель сфокусирована на бизнес-логике без компромиссов в пользу производительности чтения.
- Натуральное место для проекций и отчётности.
Когда CQRS не нужен
CQRS добавляет сложность: две модели, синхронизация, eventual consistency. Для простых CRUD-систем это неоправданные накладные расходы. Применять CQRS стоит когда: есть явное несоответствие между потребностями чтения и записи, система нагружена преимущественно чтениями, домен достаточно сложный чтобы оправдать отдельную write-модель.
Грег Янг, популяризировавший паттерн, предупреждает: «CQRS — это не архитектурный стиль, это инструмент для конкретной проблемы».
Частые вопросы
CQRS требует двух баз данных?
Не обязательно. В простейшей форме CQRS — разделение на уровне кода: отдельные обработчики команд и запросов, работающие с одной БД. Отдельные хранилища для read и write — опциональное усиление паттерна для масштабирования.
Как CQRS связан с DDD?
CQRS хорошо сочетается с Domain-Driven Design: агрегаты на write-стороне реализуют доменные инварианты, а read-стороны дают плоские проекции для UI. Event Sourcing как хранилище write-стороны дополняет оба паттерна.
Что такое проекция в CQRS?
Проекция — это денормализованное представление данных, построенное для конкретного сценария чтения. Создаётся из событий (в Event Sourcing) или команд (в CQRS без ES). Может храниться в любом хранилище: SQL, MongoDB, Redis, Elasticsearch — в зависимости от потребностей запроса.
Другие термины в теме «Архитектура ПО»
Не хватает деталей?
Напишите, что уточнить по теме «cqrs» — это помогает улучшать материал и подсказывает, какие термины добавить дальше. Email необязателен: укажите, если хотите ответ только для вас (мы не шлём рассылки).