はじめに...
新卒2年目。
まだまだ知らない横文字が飛び交うことがあります。
その中でもひときわかっこいい、CQRSについて勉強して自分なりにまとめてみました()
CQRSとは?
「読み取り」と「書き込み」の処理を完全に分けるという考え方
ですっ!
「それだけ?」
「なぜ分けるの?」
こんな疑問を自分は持ちました。
身近な例で考えてると...
・読み取り(Query):商品一覧を見る、自分の購入履歴を見る
・書き込み(Command):商品をカートに入れる、注文を確定する
という風になります。
(過去の私)「ぱっと見、大差なくね?」
(今日からの私)「実は、目的も求められる性能も全然違うんだぜ」
1. 「読み取り」はシンプル、でも大量!
目的: ユーザーに素早く情報を届けること。
求められる性能:
高速性: 商品一覧は1秒でも早く表示!!
並列処理: 何千人が同時に商品一覧を見ても、大丈夫なように!!
2. 「書き込み」は複雑、でも比較的少数!
目的: データの状態を正しく変更すること。
求められる性能:
正確性: 注文処理は絶対に失敗してはいけません。在庫がマイナスになったり、決済が二重になったりするのは大問題です。
整合性: 複数の処理が同時に発生しても、データの矛盾が起こらないこと。
なんで分けたほうがいいの??
❌ 従来のやり方で起きる問題
-
クラスがゴチャゴチャになる
読み取りには不要な情報も、書き込みには必要な情報も全部同じクラスへ。
すると、クラスは複雑になり、保守が大変に...
≒ゴミ箱 -
読み書きのトレードオフ
「読み取り」を速くするために最適化すると、「書き込み」の複雑な処理が遅くなったり、「書き込み」の整合性を保つためのロジックを入れると、「読み取り」の速度が落ちる..
「なにかを得るためには、なにかを犠牲にするしか無いのか...」
そんなときに!!
~CQRSが実現すること~(マニフェスト風)
「読み取り」と「書き込み」を完全に分離し、それぞれに最適なモデルとデータベースを用意することで、両方の要求を同時に満たすことを目指します。
-
「読み取り」側
ユーザーに見せるためのデータだけに特化した、シンプルなモデルとデータベースを用意します。複雑な結合処理も不要なので、大量のアクセスがあっても高速にデータを返せます。 -
「書き込み」側
注文処理などの複雑なロジックを正確に実行するためのモデルとデータベースを用意します。こちらはデータの一貫性を最優先に設計します。
上記のようにすることで、システム全体のパフォーマンスと信頼性を向上させることができます。
コマンド(Command): データを変更する操作(新規作成、更新、削除)
クエリ(Query): データを取得する操作(参照)
責務分離(Responsibility Segregation): この2つの操作を、独立したモデルやデータベースで扱うこと。
略してCQRS!!
(過去の私)じゃあ、なんでもCQRS使えば綺麗になるやん、メリットしかないやん
CQRSパターンのデメリット
-
複雑性の増加:
読み取りと書き込みが分離されるため、システム全体の構造が複雑に。 -
データの最終的な一貫性(Eventual Consistency):
アプリケーションだけでなく、DBサーバーもこのアーキテクチャを使用する場合、
データベース間の同期に遅延が生じるため、一時的にデータが不整合な状態になる可能性があり
「実例に落としてみる」
例えば「図書館システム」を考えてみます。
(本を借りたり返したりするアプリ的なやつ📚)
読み取り(Query)側の処理
- 本の一覧を表示する
- いま借りてる本を確認する
- 人気の本ランキングを見る
速くて見やすいことが大事
多少「ランキングが1分遅れ」とかでもOK👌
書き込み(Command)側の処理
- 本を借りる
- 本を返す
- 新しい本を追加する
これらは 失敗したら困る処理。
「借りたのに在庫が戻らない」とか「二重で借りれる」とか❌️
なので、トランザクションや整合性チェックが必須
↓構造は以下のようなかんじ!
app/
├── Commands/
│ ├── Handlers/
│ │ ├── BorrowBookHandler.php
│ │ ├── ReturnBookHandler.php
│ │ └── AddBookHandler.php
│ ├── BorrowBookCommand.php
│ ├── ReturnBookCommand.php
│ └── AddBookCommand.php
├── Queries/
│ ├── Handlers/
│ │ ├── ListBooksHandler.php
│ │ ├── MyBooksHandler.php
│ │ └── PopularBooksHandler.php
│ ├── ListBooksQuery.php
│ ├── MyBooksQuery.php
│ └── PopularBooksQuery.php
├── Models/
│ ├── Book.php
│ └── User.php
└── Http/
└── Controllers/
├── BookCommandController.php
└── BookQueryController.php
このようにすれば、クラスが簡略化され、責務が分離でき、最適化したコーディングが可能になります
実際にCQRSの考えを用いて実務を行ってみましたが、
コードの可読性が良くなったことをとても実感しました!