イベントソーシングとCQRS(Command Query Responsibility Segregation)は、しばしば一緒に語られる関連性の高いアーキテクチャパターンですが、両者は独立した異なる概念です。
CQRSとは「コマンド(書き込み操作)」と「クエリ(読み取り操作)」の責務を分離するパターンであり、イベントソーシングとは相補的な関係にあります。
両者を正確に理解して適切に組み合わせることで、スケーラブルで保守性の高い複雑なシステムを設計できます。
本記事では、イベントソーシングとCQRSの違い、関係性と組み合わせ方法、Command Query Responsibility Segregation・アーキテクチャパターン・読み書き分離・実装方法などについて解説していきます。
CQRSはコマンドとクエリを分離するパターンでありイベントソーシングとは独立した概念
それではまず、CQRSの基本概念とイベントソーシングとの根本的な違いについて解説していきます。
CQRS(Command Query Responsibility Segregation)とは、システムへの操作を「状態を変化させるコマンド(Command)」と「状態を読み取るクエリ(Query)」に明確に分離し、それぞれに最適化された実装モデルを持つアーキテクチャパターンです。
CRSの考え方は、Bertrand Meyerが提唱したCQS(Command Query Separation)原則を拡張したもので、Greg Youngによってアーキテクチャパターンとして体系化されました。
CQRSの基本構造:
Write Side(コマンドサイド):
CreateOrderCommand → OrderCommandHandler → ドメインモデル → イベント発行
Read Side(クエリサイド):
GetOrderQuery → OrderQueryHandler → リードモデル(最適化されたDB)→ DTOを返す
書き込み側(コマンドサイド)はビジネスロジックの整合性を重視した正規化されたドメインモデルを使い、読み取り側(クエリサイド)は読み取りパフォーマンスに最適化した非正規化のリードモデルを使います。
イベントソーシングとCQRSの違い
イベントソーシングとCQRSはしばしば混同されますが、本質的に異なるパターンです。
| 項目 | イベントソーシング | CQRS |
|---|---|---|
| 対象 | データの永続化方法 | 操作モデルの設計方法 |
| 問いかけ | 「どのようにデータを保存するか」 | 「どのように操作を設計するか」 |
| 独立性 | CQRSなしでも使用可能 | イベントソーシングなしでも使用可能 |
| 組み合わせ | CQRSと組み合わせると相乗効果 | イベントソーシングと組み合わせると相乗効果 |
つまり「イベントソーシング+CQRS」という組み合わせは非常に一般的ですが、それぞれ独立して採用することも可能です。
プロジェクション(リードモデルの構築)
CQRSのリードモデルは「プロジェクション(Projection)」と呼ばれるプロセスで構築・更新されます。
イベントソーシングと組み合わせた場合、イベントストアに書き込まれたドメインイベントをプロジェクションハンドラーが受け取り、読み取り専用DBに反映します。
プロジェクションは複数作成可能であり、異なる視点(ユーザー向けUI用・バックオフィス用・分析用など)に最適化した複数のリードモデルを同一イベントストリームから生成できます。
プロジェクションを再実行することで、過去のイベントから新しいリードモデルを構築したり、既存のリードモデルを修正したりできることがイベントソーシング×CQRSの大きな強みです。
イベントソーシングとCQRSを組み合わせたシステム設計
続いては、イベントソーシングとCQRSを実際に組み合わせたシステム設計の具体的なアプローチを確認していきます。
コマンドハンドラーとドメインモデルの実装
コマンドサイドでは、コマンドオブジェクトを受け取るコマンドハンドラーが、集約(Aggregate)に対してビジネスロジックを実行し、ドメインイベントを生成してイベントストアに保存します。
コマンドは「OrderItem(商品を注文する)」のような意図を表す命令形の名前を持ち、1つのコマンドハンドラーが対応します。
ドメインモデルはビジネスルールの検証(在庫確認・残高チェック等)を担い、コマンドの実行可否を判断します。
クエリサイドの設計とリードモデル
クエリサイドでは、読み取りパフォーマンスに最適化したリードモデル(非正規化テーブル・エラスティックサーチのインデックス等)を使います。
リードモデルはUIの要件に合わせて設計されるため、複雑なJOINやN+1問題を気にせずシンプルなSELECTで必要なデータを取得できます。
プロジェクションハンドラーはイベントを受け取るたびにリードモデルを更新するため、リードモデルは結果整合性を持ちます。
最終的整合性(Eventual Consistency)への対処
イベントソーシング×CQRSシステムでは、コマンドの実行(書き込み)からリードモデルへの反映(読み取り)まで短時間の遅延(レイテンシ)が発生する最終的整合性が生じます。
ほとんどの場合はミリ秒〜秒単位の遅延に収まりますが、UIでの「書き込み直後に読み取ると古いデータが見える」という問題への対処が必要です。
楽観的なUI更新(書き込みコマンドの内容をすぐにUIに反映してからリードモデルの更新を待つ)や、最終的整合性をユーザーに説明するUI設計などが対処法として一般的です。
まとめ
本記事では、イベントソーシングとCQRSの違い、関係性と組み合わせ方法、読み書き分離・実装方法などについて解説しました。
イベントソーシングは「どのようにデータを保存するか」、CQRSは「どのように操作を設計するか」という独立した問いに答えるパターンであり、組み合わせることで相乗効果を発揮します。
プロジェクションによる複数リードモデルの構築・コマンドとクエリの明確な責務分離・最終的整合性への適切な対処が、イベントソーシング×CQRSシステムの設計における重要なポイントです。
両パターンの概念を正確に理解し、システムの要件と複雑さに見合った適切な設計を選択することが、長期的に成功するアーキテクチャへの道となるでしょう。