Ремарка от меня. Подобрать терминологию было непросто, поэтому готов в процессе редактировать перевод, чтобы улучшить понимание текста.

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

CQRS Разделение ответственности команд и запросов (Command and Query Responsibility Segregation)

Чтобы начать с CQRS, просто создайте два объекта, которые ранее были одним. Разделение основано на том что методы это команды или запросы (то же определение что использовано в Meyer в Command and Query Separation, команда это любой метод, который изменяет состояние, и запрос это любой метод, который возвращает значение).

Когда большинство людей обсуждают CQRS, они действительно говорят о применении подхода к объекту, который представляет собой сервисную службу приложения. Рассмотрим следующий псевдо-код сервиса.

CustomerService

void MakeCustomerPreferred(CustomerId)
Customer GetCustomer(CustomerId)
CustomerSet GetCustomersWithName(Name)
CustomerSet GetPreferredCustomers()
void ChangeCustomerLocale(CustomerId, NewLocale)
void CreateCustomer(Customer)
void EditCustomerDetails(CustomerDetails)

Применение CQRS на этом приведет к двум сервисам

CustomerWriteService

void MakeCustomerPreferred(CustomerId)
void ChangeCustomerLocale(CustomerId, NewLocale)
void CreateCustomer(Customer)
void EditCustomerDetails(CustomerDetails)

CustomerReadService

Customer GetCustomer(CustomerId)
CustomerSet GetCustomersWithName(Name)
CustomerSet GetPreferredCustomers()

Вот и все. Это вся картина CQRS шаблона. Нет ничего более, чем это… Не кажется уже столь интересным когда мы объясняем это так, не так ли? Это разделение, позволяет сделать нам много интересного в архитектурном отношении. Самое большое, что оно разрывает процесс умственной деградации, потому что два используют те же данные, они также должны использовать ту же модель данных.

Наибольшее возможное преимущество, однако что он признает, что существуют различные архитектурные свойства при работе с командами и запросами… например, он позволяет нам разместить две службы по-разному, например: мы можем разместить сервис чтения на 25 серверах, а сервис записи на двух. Обработка команд и запросов принципиально асимметричная, а симметричное масштабирование этих сервисов не имеет большой смысл.

UI, основанный на заданиях (Task based UI)

Пользовательский интерфейс, основанный на заданиях, довольно сильно отличается от основанного на CRUD. В нем вы следите, что делает пользователь и продвигаете команды, которые представляют его намерения. Я хотел бы заявить, раз и навсегда что CQRS не требует интерфейса основанного на заданиях. Можно применить CQRS в CRUD приложении (хотя такие вещи как создание отдельных моделей будет гораздо сложнее).

Существует однако одна вещь, которая действительно требует такой интерфейс — это доменная модель.

Сервисный слой приложения в доменной модели представляет собой задачи которые система может выполнять. Это не просто скопировать данные в доменный объект и сохранить его… Следует иметь в виду, что важно именно поведение с объектом. Прежде чем двигаться дальше, давайте посмотрим, что случится, если мы все-таки так сделаем.

В нашем вездесущем языке не было бы глаголов кроме «Создать», «Удалить» и «Изменить». Пока у вас много таких моделей, в которых вездесущий язык только такой, вам, наверное, не стоит использовать принцип доменных моделей для построения системы.

Понятие о таком интерфейсе не предполагает быть частью CQRS, но это не так. Домен может иметь глаголы, но также он должен следить за намерениями пользователя, в целом это важно. Было ли это принудительным или нормальным обновлением? Какая разница? Зависит от того, какой вопрос вы хотели спросить…

Двигаемся к следующему шаблону, который вводит в заблуждение в CQRS

Источники событий

Хочу чтобы было ясно, когда я использую этот термин, я не охватываю все что написано в bliki. Я ссылаюсь к хранению текущего состояния, как серии событий и возобновляю состояние системы повторяя эти серии событий…

На командной стороне уравнения, с тех пор как «чтения» больше не часть домена, сохранение событий может быть отличным способом поддержания текущего состояния. Значение возрастет больше если вы решите иметь две отдельные модели (модель для чтения и модель для записи) и у вас есть необходимость в их интеграции, так что вы вероятнее всего будете это делать через события. Так как вы все равно сохраняете событийность, почему бы просто не использовать одну модель для управления состоянием?

Шаблон обмена сообщениями

Нету нужды в использовании этого подхода с CQRS. Впрочем, если вы разделите ваши модели данных, скорее всего вы будете использовать обмен сообщениями между ними, потому что он предлагает интересные возможности.

Наконец я подошел к последнему «подходу», не люблю его так называть, потому что в действительности это концепция, которую люди кладут в своих определениях CQRS, которая идет рука в руку с обменом сообщениями.

Конечная согласованность

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

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

Комментарии (8)


  1. AigizK
    25.10.2015 14:34
    +1

    Хотел написать что написали какую то херню. Потом посмотрел на автора. Этот чувак создал geteventstore.com
    Если мы рассмотрим CQRS как это написано в вики «метод должен быть либо командой, выполняющей какое-то действие, либо запросом, возвращающим данные, но не одновременно», то он прав. Но только ради этого естественно смысла нет использовать CQRS.
    В тех проектах где я сталкивался, всегда использовали:
    1. команды — которые создают события(и наоборот), но события важнее
    2. база для хранения событий, и вторая база, которую можно получить пробегаясь по всем событиям из первой базы.

    Без них лично я не вижу смысла использовать CQRS.


    1. lair
      25.10.2015 15:29

      Этот чувак создал geteventstore.com

      … этот «чувак» первым озвучил термин CQRS, уж если на то пошло.

      Но только ради этого естественно смысла нет использовать CQRS.

      Почему?


      1. AigizK
        25.10.2015 15:44

        Чувак этот крутой, я в курсе )) У меня 2 проекта используют его EventStore. А вот на другом проекте не смогли перейти, стрессовое тестирование не прошел.
        По поводу «почему». Какой смысл усложнять себе жизнь? Ведь клиент отправляя команду, как правило хочет знать, выполнилась она или нет, и если выполнилась то обновить у себя состояние. Проще тогда все делать «по старинке».
        А у вас есть другое мнение? Очень интересно послушать.


        1. VolCh
          25.10.2015 19:24

          Очень часто либо обновленное состояние не нужно вообще (особенно, если говорить о программных клиентах, а не пользовательских), либо клиент и сам его знает (когда команда по суди имеет семантику «обновить сущность на сервере в соответствии с её состоянием на клиенте» или проще «сохранить изменения»).


          1. AigizK
            25.10.2015 19:44

            Ну хорошо, поставим вопрос с другой стороны. Какой плюс в использование CQRS в чистом виде?


            1. lair
              25.10.2015 21:48
              +1

              Возможность последующего масштабирования, например.


            1. mird
              26.10.2015 11:33
              +1

              Более быстрые запросы которые просто отдают данные для отображения и при этом полнофункциональная доменная модель для команд. Очень, знаете ли неудобно, когда для отображения грида в котором 3-4 колонки со значениями, для каждой строки приходится загружать весь агрегат целиком.
              При этом query модель вообще не позволяет апдейтить сущности в хранилище, и поэтому нельзя испортить консистентность данных сохранив не прошедшие валидацию доменом значения.


        1. lair
          25.10.2015 21:48
          +1

          Во-первых, есть домены, где CQRS естественнен: всякое логгирование, аудит и так далее.
          Во-вторых, есть технические ситуации, когда клиенту, на самом деле, никто не гарантирует ответа об успешности команды (все удаленные подключения в условиях нестабильной связи).