👏

CQRSとCQSの違い

2022/04/11に公開

こんにちは。株式会社プラハCEOの松原です

先日プラハチャレンジで「CQSとCQRSって何が違うんだろうね?」と話し合ったので内容をまとめてみます。

結論:CQRSとCQSの違い

  • CQSはオブジェクト単位でメソッドの責務を更新と取得に応じて明確に分離すること
  • CQRSはそれをアーキテクチャレベルに適用したもの。ただ、データソースの分離を行うか〜など分離レベルに関する定義は様々存在した

CQSの定義

我らがMartin Fowler氏によればCQSという用語自体が登場したのはBertrand Meyer氏の書籍で、

The fundamental idea is that we should divide an object's methods into two sharply separated categories:

と記載の通り、「オブジェクトのメソッド」を2つのカテゴリ(クエリとコマンド)に分けること、とある。

ちなみにMeyer氏はEiffelを開発した方で、自身が作成したEiffelの説明資料にはこんなことが書いてある

データを取得したいだけなのに副作用としてデータ自身が変わっちゃダメだよね、ということ。配列に対するpopはその最たる例で、もしpopの仕様を知らないエンジニアがデータ取得のためにpopを連発していたら元の配列は意図せず空っぽになってしまいます。

popみたいに大抵のエンジニアがすでに挙動を知っている一般的なメソッドならまだしも、自分たちで書いた(つまり初見では誰も仕様を理解していない)メソッドが呼び出しの度に意図しないデータ更新を行ってしまうと事故る確率が高まるため、更新と取得は分離しようぜ、という発想。

CQSに関してはいろんな文献に目を通しても定義にさほどブレはなく、オブジェクトのメソッド単位での設計指針を指しているように感じました。

CQRSの定義

CQSに対してCQRSは結構いろんな定義が転がっていて、Martin Fowler氏曰くCQRSの定義を初めて知ったのはGreg Young氏によるこちらの資料とのこと。「ファウラーさんが言ってることが正なんだぜ」と言いたいわけではないのですが、こちらの資料に勝るソースを僕自身が見つけられていないため一旦はこちらの定義をもとに話を進めてみたいと思います。

こちらの資料を読んでいると、Young氏自身も

Command and Query Responsibility Segregation was originally considered just to be an extension of this concept. For a long time it was discussed simply as CQS at a higher level. Eventually after much confusion between the two concepts it was correctly deemed to be a different pattern.

CQRSは長いこと「CQSをより高いレイヤーで適用したもの」と解釈されてきたものの、実際は異なるパターンであったことが長年の混乱から浮き彫りになった、とまとめています。

実際じゃあCQRSって何なの、というと

Command and Query Responsibility Segregation uses the same definition of Commands and Queries that Meyer used and maintains the viewpoint that they should be pure. The fundamental difference is that in CQRS objects are split into two objects, one containing the Commands one containing the Queries.

The pattern although not very interesting in and of itself becomes extremely interesting when viewed from an architectural point of view.

基本はCQSだけど、1番の違いはCQRSにおいて1つのオブジェクトをコマンド用オブジェクトとクエリ用オブジェクトに分離すること。それ自体に大した面白みはないがアーキテクチャの観点から見た時に興味深いことが起きる、とあります。

CQRSの副作用と、それが定義に及ぼした影響

By applying CQRS the concepts of Reads and Writes have been separated. It really begs the question of whether the two should exist reading the same data model or perhaps they can be treated as if they were two integrated systems,

その1つとして、データモデルの分離が進むことで、コマンドは正規化されたRDBテーブルに整合性を担保する形で格納するのが適切だけど、クエリは整合性よりスケーラビリティや応答性を優先して非正規化されたキャッシュやリードレプリカでも事足りるよね、というデータソースレベルの分離が可能になる点もが言及されています。

そもそも全く異なる責務を持つ2つのシステム(コマンドとクエリ)が存在するのであれば、それらが1つのデータモデルを共有するより2つのシステムが統合せれている状態として表現した方が自然ではないか、と。

これはUdi Dahan氏によるCQRSの説明でも、ユーザーがみる情報はそもそもデータベースと比較して必ず整合性が破綻している(データを見ている間に他の人に更新される可能性がある)からコマンドとクエリを同じデータソースで満たす必要はないよね、という似た説明が見つかりますね。

この「データソースを分離するか」がCQRSの定義に含まれている記述がチラホラ見受けられる気がして、例えば 実践ドメイン駆動設計ではCQRSについて以下のように書かれています(実際書かれている内容を少しサマってます)

CQRSという奇妙な名前のアーキテクチャパターンはオブジェクトを設計するときの厳しい減速でやるコマンドとクエリの分離をアーキテクチャパターンに持ち込んだものである

クエリモデル

  • 非正規化したデータモデルである。 仮にこのデータモデルが SQL データベースだったとするとクライアントのビューの種類ごとにテーブルを用意することになる

コマンドモデル

  • コマンドの最後にドメインイベントを発行してクエリモデルとの整合性を担保しなければいけない

「クライアントのビュー(≒画面)の種類ごとにテーブルを用意することになる」といった記述から、こちらの書籍ではオブジェクトの分離にとどまらず、それによる副次的な効果として挙げられていたデータソースの責務分離まで定義が及んでいるように見えますね。

ドメイン駆動設計 サンプルコード&FAQでもCQRSについては以下のように

「厳密なCQRSはデータソース分離や永続化の形をイベント型にする」といった記述があり、こちらもオブジェクトの分離を超えた範囲までCQRSの定義として言及されています

まとめ

プラハチャレンジの議論では各々が文献を持ち寄って議論したのですが、果たしてどこまでが厳密なCQRSの定義に含まれるのか断言できる状態には至りませんでした。

これ以上時間をかけて正しい定義を明らかにすることでめちゃくちゃ大きな学びを得られる予感はしなかったためこの辺りが話が終了したのですが、今は個人的にはCQRS自体の定義は「オブジェクトをコマンオブジェクトドとクエリオブジェクトに分離すること」で、明確に責務が分離したシステムが登場する副次的な効果としてデータベースレベルでの分離なども視野に入ってくる、みたいな理解に落ち着いています。

(PR)
こんな議論を繰り返しながら負債を最小限に留めたコードを書く技術を学びたい方が居たら、ぜひプラハチャレンジに参加してみてね...!

蛇足:思ったこと

ここから先はポエムなので無視していただいて大丈夫です。

今回CQRSの定義を調べているうちに「明確な定義を知ることには何の意味があるんだろう?」というテーマについても会話がありました

ちょっと話は逸れるのですが、「分かる」の語源は「分ける」だそうです。つまりリンゴを見て「リンゴだ」としか感じない人もいれば「あれは紅玉だ」と分かる人もいるわけで、リンゴについて分かる、というのはリンゴをどこまで細かく区別できるか、とも言い換えられる気がします。細かく区別するためにはそれぞれのリンゴの定義を知る必要があるので、僕たちは区別するために定義を学ぶのかな、と考えた次第です。

じゃあどこまで細かく区別できれば良いかと考えると、これは「自分に役立つレベルまで区別できれば良い」という実利主義で考えています。例えば一般人にとっては「リンゴだ」と区別するだけでも生活に支障はありませんが、青果店が紅玉をリンゴとしか区別できなければ困りますよね。なので自分が置かれている状況次第で必要な区別の粒度は変わってくるのかなと。

今回のCQRSに関しても「クエリとコマンドを分けること」と定義することもできれば「データモデルの分離まで含まないためこれは軽量CQRS」と定義することもできるわけで、後者の方がCQRSをより細かく区別できている状態です。細かく区別できることで「せっかくオブジェクトを分割したのであればデータベースまで分離することを考慮しても良いのではないか?」と思考を発展して、より良い結論を導ける可能性があります。

とはいえ全人類が合意できる定義をまとめようとすると膨大な時間がかかります。抽象的な概念に万人が納得できる定義を見つけることの難しさを実感したければ、「オブジェクト指向って何ですか?」とツイートしてみるのがオススメです。とんでもない数のマサカリが飛び交います。

なので

  • 定義を知れば区別できる(分かる)
  • 区別できれば役に立つこともある
  • とはいえ完璧な定義を求めると時間がかかる

自分たちに役立つレベルまで区別できるようになったら適度なタイミングで切り上げて、次の定義を学び、また新たなものを区別できるようにしていくのが効率良いのかな〜なんてことを感じました。

アガルートテクノロジーズ/PrAha

Discussion