Command and Query Responsibility Segregation (сокращенно – CQRS) – паттерн разделения ответственности в коде (обрабатывающем обращения к серверу) на команды и запросы получения данных. Основан на принципе программирования Command-query Separation (CQS) , который сформулирован Бертандом Майером, создателем языка Eiffel.
Такое разделение вносит свои особенности.
- Команды (Command) ориентированы на задачи. Например “добавить товар в корзину”, “оформить заказ” и т.п. Они изменяют данные прикладной области в БД. Команды могут быть как синхронными (в ответ возвращая успех операции или идентификатор добавленной сущности) так и асинхронными (помещаться в очередь для последующей обработки).
- Запросы (Query) служат только для получения данных. Они не меняют данные прикладной области в БД. В ответ на запрос обычно отдается Data Transfer Object инкапсулирующий нужную информацию.
При реализации CQRS часто разделяют данные для чтения и записи по разным БД или таблицам. Подходы здесь могут применяться разнообразные. Например, использование Materialezed Views в качестве источника для чтения данных. Или использования реляционной БД для записи и NoSQL БД для чтения. Такие решения позволяют избавиться у операций чтения от сложных объединений таблиц, подзапросов и группировок.
Схема подхода CQRS представлена на рисунке ниже
Здесь трудное место – синхронизация БД чтения с данными БД записи. Обычно синхронизация достигается посредством событий в момент изменения БД записи. Тоесть в одной операции происходит как изменение данных основной БД так и отправка события на обновление данных в БД чтения. Процесс синхронизации БД чтения и записи называют проекцией.
Перечислим различные стороны паттерна CQRS.
Плюсы:
- Возможность выдерживать большие нагрузки при операциях чтения из-за ухода от ресурсоёмких запросов к БД
- Независимое масштабирование операций чтения и записи
- Более простые запросы чтения за счет плоской (без ссылок на другие таблицы) БД чтения
- Более простые классы за счет разделения ответственности, а значит проще обслуживать такой код
- Большая гибкость описания прав доступа, так как можно отдельно прописывать права для обращений на чтение и запись
Минусы:
- Не смотря на простоту формулировки CQRS сложность может крыться в деталях. Например, в организации механизма синхронизации БД чтения с БД записи
- Нужно организовывать подсистему обработки событий
- Проблема загрузки пользователем устаревших данных, если БД чтения отстает от БД записи
Как можно заметить, данный подход выглядит несколько противоречивым, так как вроде бы обещают простоту и в то же время появляются дополнительные сложности в другом месте.