Что такое CQRS и когда разделение чтения/записи оправдано

8 минут чтения
Средний рейтинг статьи — 4.8

По мере роста приложения нагрузка на базу данных становится всё более неравномерной. Одни операции постоянно читают данные, другие изменяют их. Со временем универсальная модель работы с одной и той же схемой данных начинает ограничивать производительность и усложнять развитие системы.

Для решения этой проблемы существует архитектурный подход под названием CQRS.

Несмотря на сложное название, его идея довольно проста: разделить операции чтения и записи, чтобы каждая из них могла развиваться независимо.

Что такое CQRS

CQRS (Command Query Responsibility Segregation) — это архитектурный паттерн, предложенный Грегом Янгом.

Его основная идея заключается в том, что операции изменения данных и их получения выполняются разными компонентами.

Упрощённо схема выглядит так:

Commands

 Write Model

 Database

 Read Model

 Queries

Вместо одного универсального слоя работы с данными появляются две независимые модели:

  • Command — отвечает за изменение состояния системы;
  • Query — отвечает только за чтение данных.

Почему одной модели бывает недостаточно

В обычном CRUD-приложении одна и та же модель используется для всего.

Например:

  • создание заказа;
  • изменение заказа;
  • просмотр списка заказов;
  • построение аналитики.

Со временем возникают проблемы.

Для записи требуется строгая валидация, транзакции и контроль целостности данных.

Для чтения, наоборот, важнее скорость, возможность объединять данные из разных источников и выполнять сложные выборки.

Одна модель начинает пытаться одновременно решать две совершенно разные задачи.

CQRS предлагает разделить эти обязанности.

Как работает запись

Все операции изменения проходят через Command.

Например:

Создать заказ
Изменить адрес
Отменить заказ

Команда выполняет бизнес-логику, проверяет ограничения и сохраняет изменения в основной базе данных.

Именно эта часть системы считается источником истины.

Как работает чтение

После изменения данных информация передаётся в отдельную модель чтения.

Она может выглядеть совершенно иначе.

Например:

  • содержать заранее объединённые таблицы;
  • использовать денормализованные структуры;
  • храниться в другой базе данных;
  • индексироваться для быстрого поиска.

Благодаря этому запросы выполняются значительно быстрее.

При этом модель чтения не обязана повторять структуру основной базы.

Почему чтение становится быстрее

Представим страницу интернет-магазина.

Для её отображения нужны:

  • товары;
  • остатки;
  • рейтинг;
  • фотографии;
  • скидки;
  • количество заказов.

В обычной базе такой запрос может выполнять множество JOIN-операций.

В CQRS можно заранее подготовить отдельную модель чтения, где вся эта информация уже собрана в удобном виде.

В результате приложение получает данные практически без дополнительной обработки.

Eventual Consistency

Одна из особенностей CQRS — чтение не всегда происходит из самых свежих данных.

После выполнения команды изменения могут попасть в модель чтения не мгновенно, а через несколько миллисекунд или секунд.

Такой подход называется Eventual Consistency.

Для большинства систем это не проблема.

Например, если после оформления заказа список обновится через секунду, пользователь этого даже не заметит.

Однако существуют сценарии, где подобная задержка недопустима.

Когда CQRS действительно оправдан

CQRS имеет смысл, если:

  • операций чтения значительно больше, чем записи;
  • запросы очень сложные;
  • необходимо независимо масштабировать чтение;
  • используются разные типы баз данных;
  • приложение состоит из множества микросервисов.

Подобный подход часто встречается в:

  • интернет-магазинах;
  • банковских системах;
  • CRM;
  • аналитических сервисах;
  • крупных SaaS-платформах.

Когда CQRS лучше не использовать

Для небольших проектов CQRS зачастую оказывается излишне сложным.

Он добавляет:

  • дополнительные сервисы;
  • синхронизацию моделей;
  • обработку событий;
  • новые сценарии отказов;
  • более сложную отладку.

Если приложение представляет собой обычный CRUD с несколькими таблицами, классическая архитектура обычно оказывается проще и эффективнее.

CQRS и Event Sourcing

Эти понятия часто упоминают вместе, хотя они не являются одним и тем же.

CQRS определяет разделение чтения и записи.

Event Sourcing определяет способ хранения данных — не в виде текущего состояния, а как последовательность произошедших событий.

Эти подходы хорошо сочетаются, но могут использоваться и независимо друг от друга.

Типичные ошибки

При внедрении CQRS разработчики часто:

  • используют его без реальной необходимости;
  • пытаются разделить даже самые простые CRUD-сервисы;
  • забывают учитывать задержку синхронизации моделей;
  • усложняют архитектуру раньше времени;
  • создают несколько моделей данных, которые затем сложно поддерживать.

CQRS приносит пользу только тогда, когда его сложность окупается требованиями системы.

Итоги

CQRS — это архитектурный паттерн, который разделяет операции чтения и записи на независимые модели.

Такой подход позволяет оптимизировать каждую часть системы под свою задачу, ускорить выполнение сложных запросов и масштабировать чтение отдельно от записи.

Однако CQRS — не универсальное решение. Он делает архитектуру сложнее, требует синхронизации моделей данных и вводит концепцию eventual consistency. Поэтому использовать его стоит тогда, когда приложение действительно сталкивается с высокой нагрузкой, сложными запросами или необходимостью независимого масштабирования, а не только потому, что этот паттерн популярен.

8 минут чтения
Средний рейтинг статьи — 4.8

Настроить мониторинг за 30 секунд

Надежные оповещения о даунтаймах. Без ложных срабатываний