CQRS (Command Query Responsibility Segregation) — один из тех паттернов, которые часто описывают как обязательный спутник event sourcing и микросервисов. На практике это самостоятельный принцип, который может быть полезен и в монолите, и без event sourcing, и даже без отдельных баз данных.
Суть проста: модель для записи (commands) и модель для чтения (queries) — это разные модели. Они могут жить в одной базе, но обслуживают разные потребности и оптимизированы под разные сценарии.
Вопрос не в том, «нужен ли CQRS», а в том, насколько далеко разводить чтение и запись — от простого разделения интерфейсов до полностью независимых хранилищ с асинхронной синхронизацией.
В статье
- Что такое CQRS и чем он не является
- Три уровня разделения
- CQRS + Event Sourcing: естественная, но не обязательная связка
- Когда CQRS оправдан
- Где CQRS создаёт лишнюю сложность
- Лунная база: зачем разделять модель управления и мониторинга
- Eventual consistency: с чем придётся жить
- Checklist: нужен ли вам CQRS
Что такое CQRS и чем он не является
CQRS разделяет операции на два типа:
- Command — намерение изменить состояние (
AllocateReserve,ConsumeResource). Может быть отклонена. Не возвращает данные (или возвращает только ID/статус). - Query — запрос данных (
GetCurrentResourceLevel,ListRecentConsumption). Не меняет состояние. Идемпотентна.
Это не означает:
- две обязательные базы данных;
- обязательный event sourcing;
- обязательную асинхронность;
- обязательные микросервисы.
CQRS — это принцип проектирования, а не конкретная инфраструктура.
Три уровня разделения
Уровень 1: разделение интерфейсов
Самый лёгкий вариант. Одна БД, но API разделён: command-хендлеры и query-хендлеры — разные модули. Write-модель и read-модель — разные структуры в коде, даже если обращаются к одним таблицам.
Польза: код становится чище, обязанности не смешиваются. Стоимость: почти нулевая.
Уровень 2: разные модели, одно хранилище
Write-модель нормализована и оптимизирована для консистентности. Read-модель — денормализована, оптимизирована для быстрых запросов (materialized views, кеши). Обе живут в одной базе или кластере.
Польза: чтение ускоряется без усложнения write-пути. Стоимость: поддержка синхронизации между моделями.
Уровень 3: полное разделение хранилищ
Write и read — в разных базах. Синхронизация через события (часто вместе с event sourcing). Read-хранилище может быть PostgreSQL, Elasticsearch, Redis — что подходит под сценарий.
Польза: независимое масштабирование, оптимальные хранилища под каждую задачу. Стоимость: eventual consistency, сложность инфраструктуры, дополнительный код.
CQRS + Event Sourcing: естественная, но не обязательная связка
CQRS и event sourcing часто упоминают вместе, потому что event sourcing естественно создаёт разделение: события — это write-сторона, проекции — read-сторона.
Но:
- CQRS без event sourcing — нормально. Разделение моделей полезно и с обычными таблицами.
- Event sourcing без CQRS — возможно, но неудобно. Без отдельной read-модели придётся каждый раз replay’ить события.
Связка мощная, но если задача не требует event sourcing — CQRS можно применять изолированно.
Когда CQRS оправдан
- Сильная асимметрия нагрузки — чтений в 100× больше, чем записей (или наоборот);
- разные требования к моделям — write-модель должна быть строго нормализованной, read-модель — денормализованной для быстрых дашбордов;
- несколько представлений одних данных — одна и та же сущность нужна в разных форматах для разных потребителей;
- сложная доменная логика на записи — command-сторона содержит валидацию, бизнес-правила, инварианты, которые не нужны при чтении;
- независимое масштабирование — read-реплики без нагрузки на write-путь.
Где CQRS создаёт лишнюю сложность
- Простой CRUD — если read и write модели почти идентичны, разделение добавляет код без пользы;
- строгая консистентность на чтение — если пользователь должен сразу видеть свои изменения, eventual consistency вызовет проблемы;
- маленькая команда — поддержка двух моделей, синхронизации и мониторинга требует ресурсов;
- нет метрик — без понимания профиля нагрузки (read/write ratio, latency requirements) решение о разделении принимается наугад.
Лунная база: зачем разделять модель управления и мониторинга
В кейсе лунной базы CQRS возникает естественно:
Write-сторона (управление):
- команды на распределение ресурсов;
- переключение режимов жизнеобеспечения;
- назначение заданий роботам;
- строгие инварианты безопасности.
Read-сторона (мониторинг):
- текущее состояние всех подсистем — дашборд;
- история расхода ресурсов — графики;
- прогноз исчерпания — аналитическая модель;
- аварийные оповещения — real-time.
Каждый read-сценарий требует своей оптимизации. Пытаться обслужить их все из одной нормализованной модели — путь к медленным запросам и сложным JOIN’ам.
Eventual consistency: с чем придётся жить
При полном разделении хранилищ (уровень 3) с асинхронной синхронизацией read-модель отстаёт от write-модели на время распространения изменений. Это не свойство любого раздельного хранилища как такового — при синхронном обновлении отставания нет; но именно асинхронный вариант обычно и выбирают ради масштабирования и устойчивости к сбоям. Это значит:
- пользователь может не сразу увидеть свои изменения;
- разные read-модели могут показывать разные состояния в один момент времени;
- нужен мониторинг лага между write и read;
- в критичных сценариях может потребоваться «read your own writes» — чтение из write-модели после собственной команды.
Eventual consistency — не баг, а trade-off. Если он неприемлем для конкретного сценария, используйте уровень 1 или 2.
Checklist: нужен ли вам CQRS
- Сильно ли отличаются модели чтения и записи в вашем домене?
- Есть ли асимметрия нагрузки (read-heavy или write-heavy)?
- Нужны ли несколько разных представлений одних данных?
- Готова ли команда работать с eventual consistency (если уровень 3)?
- Есть ли метрики, подтверждающие узкое место на чтении или записи?
- Достаточно ли простого разделения интерфейсов (уровень 1) вместо полного разделения?
Начинайте с уровня 1. Переходите дальше только когда появляется измеримая причина.
Документация и первоисточники
- Канон: Martin Fowler — CQRS, Greg Young — CQRS Documents (PDF), Microsoft — CQRS pattern, microservices.io — CQRS.
- Смежное на сайте: CQRS на практикеготовится, с 17 августа (проекции, rebuild, read-your-writes), Event Sourcing и ES на практикеготовится, с 10 августа, гарантии доставки и идемпотентность, моделирование данныхСкоро (read-модель), событийная архитектура — карта, матрица решенийготовится, с 8 августа.
Комментарии