Понимание CQRS: концепция и преимущества
Подобное разделение операций нужно для более эффективной работы системы над каждой задачей. Оно приводит к повышению производительности, созданию более четкого кода и упрощению масштабирования по мере развития приложения.
Принципы разделения команд и запросов
CQRS расшифровывается как Command Query Responsibility Segregation. Это шаблон для отделения операций, которые считывают данные (запросы), от операций, записывающих данные (команды). В системе, основанной на CQRS, обычно используются следующие компоненты:
- Command — это действие по изменению состояния системы. Нужны для создания, обновления или удаления data;
- Query — проводит операцию по извлечению данных из системы. Запросы отвечают за считывание данных без их изменения;
- Command Handler — подходит для обработки и выполнения команды. Вносит изменения в состояние системы;
- Query Handler — обрабатывает запросы, извлекает информацию из системы и возвращает ее в подходящем формате.
Почему CQRS повышает эффективность работы
В традиционных системах, где как для чтения, так и для обновления используется единый набор таблиц базы данных, вы моделируете и проектируете либо скорость чтения, либо скорость записи. По мере роста приложения вам может потребоваться создавать разные представления одних и тех же данных. В итоге они будут либо создаваться очень медленно, либо выполняться в транзакции во время операции записи. Это замедлит работу.
В CQRS сторона записи, ответственная за обработку команд, сохраняет данные, используя форму базы данных с наименьшим несоответствием импеданса, чтобы увеличить пропускную способность. Когда происходят постепенные изменения состояния, механизм обрабатывает эти изменения асинхронно и создает новое представление БД. Оно адаптировано для стороны чтения. Сторона чтения, ответственная за обработку запросов, обрабатывает операции чтения и получает доступ к информации в хранилище данных для чтения.
При использовании CQRS систему проще модифицировать и корректировать с течением времени. Изменения, внесенные в модель head, не всегда могут повлиять на модель writer, и наоборот. Такое разделение облегчает эволюцию системы. Она становится более гибкой и отзывчивой, а также лучше удовлетворяет меняющиеся потребности бизнеса.
Применение паттерна CQRS на практике
Рассмотрим, как использовать паттерн для оптимизации системы.
Как реализовать командную и запросную модели
Шаг 1. Определите commands и queries.
Начните с определения команд и запросов, которые должна обрабатывать система. Команды представляют собой действия, которые изменяют состояние (например, создают заказ, обновляют информацию о клиенте), в то время как запрос извлекает данные (например, GetOrderDetails, ListCustomers).
Шаг 2. Создайте обработчики команд.
Реализуйте обработчики команд для работы с каждой из них. Они должны проверять команду, применять бизнес-правила и обновлять модель записи. Обработчики команд часто публикуют события (events), чтобы уведомлять другие составляющие системы о переменах состояния.
Шаг 3. Создайте обработчики запросов.
Обработчики запросов извлекают данные из модели чтения, которая оптимизирована для повышения производительности запросов. Моделью чтения может быть отдельная БД или денормализованное представление данных.
Шаг 4. Реализуйте поиск событий.
Поиск источников событий является обычной практикой для ведения истории изменений состояния. Вместо непосредственного сохранения текущего состояния поиск источников событий сохраняет их последовательность. Этот подход обеспечивает надежный контрольный журнал и позволяет восстанавливать состояние по событиям.
Шаг 5. Синхронизируйте модели чтения и записи.
Убедитесь, что изменения в модели записи распространяются на модель чтения. Этот шаг имеет важное значение для обеспечения согласованности между моделями.
Популярные фреймворки и инструменты для работы с CQRS
Дополнительные инструменты и платформы могут помочь в реализации шаблона CQRS в архитектуре микросервисов. Вот несколько популярных вариантов:
- Axon Framework,
- EventFlow,
- Lagom,
- Akka,
- Spring Framework.
CQRS и его связь с другими архитектурными паттернами
CQRS связан с другими архитектурными шаблонами. Например, с Task-based UI, Event Sourcing, Event Collaboration, Database per Service, Transactional Outbox.
Интеграция CQRS с Event Sourcing
Event Sourcing — это метод, часто используемый в CQRS. В традиционных системах при изменении данных (например, при обновлении профиля пользователя) вы сохраняете только конечное состояние. Например, если пользователь меняет свой адрес электронной почты, в базе данных сохраняется только новый, а старый адрес теряется.
С помощью функции поиска событий вместо сохранения только конечного состояния каждое изменение сохраняется как отдельное. Так, в случае обновления профиля пользователя вы сохраняете такие события, как «Пользователь создан», «Обновлен адрес электронной почты» или «Изменена фотография профиля». Каждое событие фиксирует, что произошло и когда. Они сохраняются в определенной последовательности — их можно сравнить с журналом.
Эти события становятся источником достоверной информации в системе. Когда выполняется команда (например, обновление профиля пользователя), создается новое событие, которое добавляется в этот список. Со временем events накапливаются и представляют всю историю изменений.
Когда вам нужно запросить или просмотреть текущее состояние системы (например, получить доступ к профилю пользователя), система может воспроизвести эти events в правильном порядке: например, «Пользователь создан» и «Электронная почта обновлена». Это поможет отобразить текущую электронную почту и информацию профиля.
Взаимодействие CQRS с микросервисной архитектурой
Микросервисы — это независимые модульные сервисы с многоуровневой архитектурой. Когда микросервисы совместно используют одну и ту же базу данных, модель данных между сервисами может соответствовать взаимосвязям между таблицами, связанными с микросервисами.
CQRS здесь будет иметь отдельный сервис, модель и базу данных для операций вставки. Он действует как командный уровень. Отдельный сервис, модель и база данных для этих запросов работают как request level.
База данных read может хранить денормализованную модель. В ней можно использовать БД, такие как NoSQL (они масштабируются по горизонтали). Уровень команд применяется для вставки данных в хранилище. Запросы же помогают извлекать информацию из него.
В микросервисе клиента, используемом в качестве командной модели, любое изменение данных о клиенте генерирует события и публикует их в очереди обмена сообщениями. Это также позволяет параллельно регистрировать события в базе данных.
Плюсы и минусы внедрения CQRS
У паттерна есть свои плюсы и минусы. Отметим несколько из них.
Улучшение производительности и управляемости данных
CQRS способствует четкому разделению задач. Команды отвечают за внесение изменений, а запросы — за поиск информации. Это упрощает понимание и поддержку кода, поскольку у каждой части системы есть четкая ответственность. Паттерн предоставляет широкий выбор для использования различных баз данных для команд и запросов. Для быстрого чтения подходит NoSQL, а реляционная БД необходима для записи. Такая гибкость позволяет выбирать лучшие инструменты для каждой задачи.
Потенциальные сложности и ограничения паттерна
CQRS часто приводит к конечной согласованности, а не к строгой. Может возникнуть задержка между выполнением операции записи и отображением изменений в модели чтения. Это может быть проблематично для приложений, требующих согласованности в реальном времени. Сложность эксплуатации может возрасти при управлении двумя базами данных или хранилищами (одно для чтения и одно для записи). Процесс включает мониторинг, резервное копирование и восстановление, а также обеспечение надежности и высокой доступности информации.