CQRS: разделение команд и запросов для повышения производительности

CQRS: разделение команд и запросов для повышения производительности
Что такое разделение ответственности команд и запросов (CQRS)?
Разделение ответственности команд и запросов (Command Query Responsibility Segregation, CQRS) — это шаблон проектирования, который отделяет действия, изменяющие данные (команды), от действий, извлекающих данные (запросы). Вместо использования одной и той же модели для обоих случаев CQRS разделяет их на два отдельных пути. Благодаря разделению команд и запросов системы могут эффективнее обрабатывать каждую задачу, что приводит к повышению производительности, более понятному коду и более простому масштабированию по мере роста вашего приложения.
Ключевые концепции CQRS
Команда
Команда — это действие, которое изменяет данные в системе, например операция записи в базе данных. Она представляет собой запрос на выполнение конкретной задачи, такой как создание, обновление или удаление чего-либо. Команды не возвращают данные; они говорят системе «сделать что-то».
Пример: когда пользователи обновляют информацию своего профиля, отправляется команда вроде “UpdateUserProfile”, чтобы изменить их имя, email или другие данные в системе.
Запрос
Запрос — это обращение для получения данных без их изменения, то есть операция чтения в базе данных. Запросы используются для получения информации, например для поиска или отображения данных. В отличие от команд, запросы только возвращают данные и ничего не изменяют.
Пример: когда пользователь хочет просмотреть данные своего профиля, запрос вроде “GetUserProfile” извлекает соответствующую информацию из системы.
Как работает CQRS?
Реальная аналогия для понимания работы CQRS — представить его как отдельные полосы для легковых и грузовых автомобилей на автомагистрали. Грузовики перевозят тяжелые грузы (команды), а легковые автомобили движутся быстро и плавно (запросы). Если держать их на отдельных полосах, движение становится более упорядоченным, и обе задачи выполняются эффективнее.
CQRS работает за счет четкого разделения обработки команд (изменений данных) и запросов (извлечения данных). Когда выдается команда, например вставка или обновление данных, она проходит по выделенному пути, предназначенному для обработки изменений. Система точно обрабатывает эти команды, часто запуская связанную бизнес-логику или события. Запросы, в свою очередь, направляются по другому пути, оптимизированному для быстрого и эффективного чтения данных.
Важно отметить, что в CQRS разделение команд и запросов не обязательно означает, что для каждого из них нужно использовать разные базы данных. Основной акцент делается на разделении поведения и ответственности, что можно сделать в рамках одной базы данных или нескольких баз данных при необходимости.
Рисунок- высокоуровневая архитектура CQRS.png
Рисунок: высокоуровневая архитектура CQRS
Пример: система регистрации пользователей
Когда пользователь регистрируется, запускается команда под названием RegisterUserCommand. Эта команда создает нового пользователя, проверяет его данные и добавляет его в базу данных. За кулисами сохраняется событие вроде `UserRegisteredEvent`, которое фиксирует это изменение.
Позже, когда другой части системы (или пользователю) нужно просмотреть данные пользователя, используется запрос под названием GetUserProfileQuery. Запрос извлекает данные профиля пользователя из базы данных, не внося никаких изменений.
CQRS и Event Sourcing
Event Sourcing — это техника, часто используемая вместе с CQRS. В традиционных системах, когда данные изменяются (например, пользователь обновляет свой профиль), мы сохраняем только конечное состояние данных. Например, если пользователь меняет свой адрес электронной почты, мы сохраняем в базе данных только новый email, а старый email теряется.
При использовании Event Sourcing вместо сохранения только конечного состояния мы сохраняем каждое изменение как отдельное событие. Так, в случае обновления профиля пользователя мы сохраняем события вроде "Пользователь создан", "Email обновлен" или "Фотография профиля изменена". Каждое событие фиксирует, что произошло и когда. Эти события сохраняются в последовательности, почти как журнал истории всего, что происходит в системе.
Эти события становятся источником истины системы. Когда выполняется команда (например, обновление профиля пользователя), создается новое событие и добавляется в этот список событий. Со временем все эти события накапливаются, представляя всю историю изменений.
Когда нужно выполнить запрос или просмотреть текущее состояние системы (например, получить профиль пользователя), система может воспроизвести эти события в правильном порядке, чтобы восстановить текущее состояние. Например, она может воспроизвести события "User created" и "Email updated", чтобы показать текущий email и детали профиля.
Разница между CQRS и CQS
Хотя CQRS (Command Query Responsibility Segregation) и CQS (Command Query Separation) являются принципами проектирования, которые подчеркивают различие между командами и запросами, они немного отличаются в некоторых аспектах.
| Аспект | CQS (Command Query Separation) | CQRS (Command Query Responsibility Segregation) |
|---|---|---|
| Определение | Методы являются либо командами (изменяют данные), либо запросами (получают данные). | Система разделяет команды и запросы на отдельные модели или пути. |
| Сложность | Простой принцип, применяемый внутри методов. | Более сложный, включает отдельные модели или слои для команд и запросов. |
| Сценарий использования | Подходит для более простых систем, где команды и запросы обрабатываются в рамках одной структуры. | Идеально для больших, сложных систем, где чтение и запись можно оптимизировать отдельно. |
| Обработка данных | Одна и та же модель данных используется как для команд, так и для запросов. | Команды и запросы могут использовать разные модели данных или даже базы данных. |
| Фокус | В первую очередь сосредоточен на том, что методы имеют единственную ответственность. | Сосредоточен на масштабируемости, производительности и разделении ответственностей по всей системе. |
| Масштабируемость | Меньше влияет на масштабируемость системы, поскольку оба типа операций обрабатываются в одной модели. | Высокая масштабируемость, поскольку команды и запросы обрабатываются независимо. |
| Гибкость | Ограниченная гибкость, поскольку обе операции связаны вместе в одной структуре. | Большая гибкость; можно использовать разные базы данных или механизмы хранения для команд и запросов. |
| Event Sourcing | Обычно не используется. | Часто используется с event sourcing для отслеживания всех изменений с течением времени. |
Таблица: разница между CQS и CQRS
Сценарии использования CQRS
Вот несколько распространенных сценариев, где CQRS полезен:
Высокие нагрузки чтения/записи
В сценариях, где существует значительный дисбаланс между количеством операций чтения и записи, CQRS является идеальным решением. Например, платформа электронной коммерции может иметь большое количество пользователей, запрашивающих информацию о товарах (чтения), но меньше пользователей, обновляющих свои корзины или совершающих покупки (записи). CQRS позволяет системе масштабировать сторону запросов независимо, чтобы обрабатывать большой объем запросов на чтение без влияния на операции записи.
Сложная бизнес-логика
При обработке сложных бизнес-правил, которые применяются только к изменениям данных (commands), CQRS помогает, изолируя эту сложность. Например, сторона command может обеспечивать строгую валидацию и правила обработки в финансовой системе, где транзакции жестко регулируются. В отличие от этого, сторона query остается простой и сосредоточенной на получении балансов счетов или историй транзакций.
Event Sourcing и журналы аудита
В системах, где необходимо поддерживать полный журнал аудита каждого изменения, CQRS хорошо сочетается с event sourcing. Например, в приложении для здравоохранения каждое изменение в медицинской карте пациента может храниться как событие, позволяя системе легко отслеживать, кто внес изменения и когда, в то время как запросы могут быстро получать доступ к текущему состоянию записи.
Аналитика в реальном времени
Для приложений, которым требуется аналитика в реальном времени, таких как платформы социальных сетей, CQRS может обрабатывать постоянные обновления пользовательских данных (например, лайки, репосты), одновременно обрабатывая запросы для получения аналитики в реальном времени (например, трендовые темы). Сторона query может быть оптимизирована для скорости без влияния со стороны текущих обновлений на стороне command.
Архитектуры микросервисов
В системах на основе микросервисов CQRS предоставляет разные сервисы для независимой обработки команд и запросов. Например, в логистической компании один сервис может управлять обработкой заказов (commands), в то время как другой занимается отслеживанием и отчетностью (queries), повышая гибкость и удобство сопровождения системы.
Инструменты и фреймворки, поддерживающие CQRS
Несколько инструментов и фреймворков упрощают внедрение CQRS. Вот краткий обзор некоторых популярных вариантов:
Axon Framework (Java) — это полнофункциональный фреймворк, поддерживающий CQRS и event sourcing. Он предоставляет встроенные компоненты для обработки команд, запросов и событий, что делает его популярным выбором для сложных Java-приложений.
MediatR — еще одна легковесная библиотека для приложений .NET, которая упрощает разделение команд и запросов. Она помогает маршрутизировать запросы чистым и организованным способом, что идеально подходит для CQRS в системах малого и среднего размера.
Laravel Command Bus является частью фреймворка Laravel. Разработчики используют этот инструмент для раздельного определения команд и запросов. Это отличный вариант для приложений на PHP, стремящихся внедрить CQRS.
EventStore — это специализированная база данных, разработанная для event sourcing, часто используемая вместе с CQRS. Она хранит события, а не просто данные, обеспечивая простой повтор прошлых событий для восстановления состояния системы.
NServiceBus — это фреймворк обмена сообщениями, который хорошо работает с CQRS, обрабатывая команды, события и сообщения в распределенных системах. Он идеально подходит для архитектур микросервисов, использующих CQRS.
Преимущества CQRS
Улучшенная масштабируемость и производительность
Разделяя команды и запросы, CQRS может масштабировать каждую часть независимо. Например, если ваше приложение обрабатывает гораздо больше операций чтения (queries), чем операций записи (commands), вы можете масштабировать сторону query, не затрагивая сторону command, улучшая общую производительность.
Более понятная организация кода
CQRS способствует четкому разделению ответственности. Команды занимаются внесением изменений, а запросы — получением данных. Это делает код проще для понимания и сопровождения, поскольку каждая часть системы имеет четкую ответственность.
Лучшая обработка сложных бизнес-правил
С CQRS сложные бизнес-правила, связанные с обновлением данных, могут обрабатываться на стороне command, тогда как для чтения данных могут использоваться более простые и эффективные запросы. Это разделение помогает управлять сложной логикой без перегрузки системы или чрезмерного усложнения кода.
Гибкость в выборе баз данных для чтения и записи данных
CQRS дает вам свободный выбор использовать разные базы данных для команд и запросов. Например, вы можете использовать высоко оптимизированную NoSQL-базу данных для быстрого чтения (запросов) и реляционную базу данных во время записей (команд). Эта гибкость позволяет выбирать лучшие инструменты для каждой задачи.
Проблемы и аспекты CQRS
Повышенная сложность
При использовании CQRS необходимо управлять двумя отдельными моделями: одной для обработки команд (записей), а другой для обработки запросов (чтения). Это добавляет сложности в кодовую базу, поскольку необходимо поддерживать и обновлять обе модели, увеличивая усилия, необходимые для разработки и сопровождения системы.
Проблемы синхронизации
Поддерживать синхронизацию сторон команд и запросов может быть непросто. Когда данные записываются через команду, может пройти некоторое время, прежде чем изменения отразятся на стороне запросов, что приводит к возможным задержкам. Управление этой синхронизацией важно для обеспечения точности данных во всей системе.
Решение о том, когда CQRS является избыточным
CQRS не нужен для простых CRUD-приложений (Create, Read, Update, Delete — создание, чтение, обновление, удаление). В системах с простыми требованиями к данным дополнительная сложность CQRS может быть неоправданной. CQRS лучше всего подходит для сложных систем с большим объемом чтений и записей или сложной бизнес-логикой.
Стоимость
Внедрение CQRS часто требует дополнительной инфраструктуры, например поддержки отдельных баз данных для команд и запросов. Это может увеличить расходы на оборудование и облачные сервисы, особенно при масштабировании обеих сторон. Необходимость использования нескольких технологий для обработки этих задач может повысить затраты.
Как CQRS играет роль в векторных базах данных и Big Data
В векторных базах данных, таких как Milvus, CQRS предлагает структурированный способ обработки различных типов операций:
Команды: они включают задачи вроде вставки или обновления больших объемов векторов (например, векторных эмбеддингов, сгенерированных моделями машинного обучения). В Milvus команды обновляют состояние базы данных.
Запросы: эти операции извлекают векторы, например находят похожие элементы в большом наборе данных с помощью поиска по сходству. Запросы Milvus могут возвращать многомерные векторы, соответствующие конкретным критериям, и часто используются в аналитике в реальном времени или AI-приложениях, таких как рекомендательные системы и retrieval augmented generation (RAG).
Пример: обработка аналитических запросов в реальном времени по сравнению со вставкой/обновлением векторов
Допустим, вы запускаете рекомендательную систему на базе AI в Zilliz Cloud (управляемой версии Milvus). Когда генерируются новые пользовательские данные или продуктовые векторы, система отправляет команды для обновления векторной базы данных свежими эмбеддингами. Эти команды обеспечивают вставку или обновление новейших векторов в Milvus.
В то же время аналитика в реальном времени выполняется с использованием запросов для извлечения наиболее похожих векторов для рекомендаций. Сторона запросов оптимизирована для быстрого поиска релевантных данных без влияния постоянных вставок векторов на стороне команд.
Заключение
CQRS предоставляет мощный способ управления сложными системами путем разделения команд (изменений данных) и запросов (извлечения данных). Это разделение улучшает масштабируемость, производительность и ясность кода, особенно в крупномасштабных приложениях со сложной бизнес-логикой. Хотя CQRS добавляет некоторую сложность, он дает значительные преимущества при использовании в подходящих сценариях, таких как системы, обрабатывающие аналитику в реальном времени, высокие нагрузки чтения/записи или event sourcing. Однако для более простых приложений дополнительная сложность может быть необязательной. В заключение, CQRS — ценный инструмент для повышения эффективности и гибкости системы при правильном применении.
FAQ по CQRS
- Каково главное преимущество использования CQRS?
CQRS разделяет обязанности по обновлению данных (команды) и получению данных (запросы), что улучшает масштабируемость системы, производительность и ясность кода. Каждая сторона может быть оптимизирована независимо, что делает этот подход полезным для высоконагруженных или сложных систем.
- Когда следует избегать использования CQRS?
CQRS может добавить ненужную сложность для простых CRUD-приложений, где нет необходимости в раздельной обработке операций чтения и записи. Если у вашего приложения нет значительных требований к производительности или сложной бизнес-логики, CQRS может быть избыточным.
- Нужно ли использовать отдельные базы данных для команд и запросов в CQRS?
Нет, использование отдельных баз данных для команд и запросов не является обязательным. Хотя в CQRS можно использовать разные базы данных, главное — разделить поведение и ответственность в коде. Вы по-прежнему можете использовать одну базу данных, если это соответствует потребностям вашего приложения.
- Как CQRS работает с event sourcing?
В event sourcing каждое изменение в системе сохраняется как событие. CQRS дополняет этот подход, обрабатывая команды, которые генерируют события, и используя запросы для восстановления состояния системы путем повторного воспроизведения этих событий. Таким образом, сохраняется полная история изменений, что упрощает аудит или отладку.
- Может ли CQRS повысить производительность приложений для аналитики в реальном времени?
Да, CQRS полезен для аналитики в реальном времени. Он позволяет системе обрабатывать непрерывные обновления (команды) отдельно от запросов данных в реальном времени, оптимизируя каждую часть для ее цели и не позволяя одной замедлять другую.
- Каковы сложности реализации CQRS?
Основные сложности включают повышенную сложность, управление синхронизацией между моделями команд и запросов, обработку проблем eventual consistency и потенциально более высокие инфраструктурные затраты из-за поддержки отдельных путей или баз данных.
Связанные ресурсы
- Что такое разделение ответственности команд и запросов (CQRS)?
- Ключевые концепции CQRS
- Как работает CQRS?
- CQRS и Event Sourcing
- Разница между CQRS и CQS
- Сценарии использования CQRS
- Инструменты и фреймворки, поддерживающие CQRS
- Преимущества CQRS
- Проблемы и аспекты CQRS
- Как CQRS играет роль в векторных базах данных и Big Data
- Заключение
- FAQ по CQRS
- Связанные ресурсы
Контент
Начните бесплатно, масштабируйтесь легко
Попробуйте полностью управляемую векторную базу данных, созданную для ваших GenAI приложений.
Попробуйте Zilliz Cloud бесплатно

