CQRSと読み取り/書き込み分離
CQRSとは
CQRS (Command Query Responsibility Segregation)は、データモデルと書き込み(コマンド)と読み取り(クエリ)を担当するコンポーネントを分離するアーキテクチャアプローチです。
アイデア:状態変更プロセスは、有効な不変とトランザクションのために最適化され、高速でターゲットを絞った予測とスケーリングのために読み取ります。
key>コマンドは状態を変更し、操作の結果を返します。リクエストは読み取りのみで、副作用はありません。
なぜあなたはそれを必要とします
パフォーマンスを読む:特定のシナリオ(テープ、レポート、カタログ)のための具体化された予測。
クリティカルパスの安定性:「重い」結合と集約から分離された記録。
ストレージ選択の自由:書き込み用OLTP、読み取り用OLAP/キャッシュ/検索エンジン。
加速された進化:トランザクションを「破る」リスクなしに新しいビューを追加します。
観察と監査(特にイベントソーシングと組み合わせて):状態を回復して再生する方が簡単です。
いつ適用するか(およびそうでない場合)
適した場合:- 異なるデータスライスと複雑な集計による読み取りが優先されます。
- 重要な録音経路は微妙で予測可能でなければなりません。
- 読み書きにはさまざまなSLO/SLAが必要です。
- 分析/検索のニーズからドメイン書き込みロジックを分離する必要があります。
- ドメインはシンプルで、負荷は低いです。CRUDは対処します。
- すべてのシナリオでは、読み取りと書き込みの間の強力な一貫性が必須です。
- チームは経験がなく、運用の複雑さは受け入れられません。
基本的な概念
Command-Intends to change state ('CreateOrder'、 'CapturePayment')。不変量をチェックします。
クエリ-データを取得します('GetOrderById'、 'ListUserTransactions')。副作用はありません。
レコードモデル:集計/不変量/トランザクション;storage-リレーショナル/キー値/イベントログ。
読み取り(投影)モデル:実体化テーブル/インデックス/キャッシュ、非同期に同期。
一貫性:多くの場合、録音と読み取りの間で最終的な;クリティカルパス-書き込みモデルからの直接読み取り。
アーキテクチャ(スケルトン)
1.Write-service:コマンドを受け付け、不変量を検証し、変更をキャプチャします(データベースまたはイベント)。
2.Outbox/CDC:変更の事実の保証された公表。
3.プロジェクションプロセッサ:イベント/CDCを聴き、読み取りモデルを更新します。
4.読み取りサービス:実体化ビュー/キャッシュ/検索からのクエリを提供します。
5.Sagas/orchestration:座標クロスアグリゲートプロセス。
6.観測性:投影遅延、成功したアプリケーションの割合、DLQ。
レコーディングモデルの設計
集計:clear transaction boundaries(例えば、'Order'、 'Payment'、 'UserBalance')。
不変量:形式化(金額≥ 0、一意性、限度)。
コマンドはキーによってidempotency(例えば'idempotency_key')です。
取引は範囲内で最小限です。外部副作用-アウトボックスを介して。
コマンドサンプル(Pseudo-JSON)
json
{
"command": "CapturePayment",
"payment_id": "pay_123",
"amount": 1000,
"currency": "EUR",
"idempotency_key": "k-789",
"trace_id": "t-abc"
}
読書モデルの設計
クエリから始める:どの画面/レポートが必要ですか?
非正規化は許容されます:read-model-「最適化されたキャッシュ」。
さまざまなタスクのいくつかの予測:検索(OpenSearch)、レポート(列ストレージ)、カード(KV/Redis)。
TTLと再構成:投影はソース(イベントリプレイ/スナップショット)から回復できる必要があります。
一貫性とUX
最終的な一貫性:インターフェイスは、短い時間の古いデータを表示することができます。
UXパターン:「データは更新されます……」、楽観的なUI、同期インジケータ、確認されるまで危険なアクションをブロックします。
強い一貫性を必要とする操作(たとえば、書き込み前に正確なバランスを示す)については、書き込みモデルから直接読み取ります。
CQRSとイベントソーシング(オプション)
Event Sourcing (ES)はイベントを保存し、集計の状態はその集約の結果です。
CQRS+ESバンドルは、理想的な監査と予測の簡単な再構成を提供しますが、複雑さが増します。
代替:通常のOLTPデータベース+outbox/CDC→投影。
レプリケーション: OutboxおよびCDC
Outbox (1つのトランザクションで):ドメイン変更を書く+outboxにイベントを書く;出版社はタイヤに渡します。
CDC:データベースログ(Debeziumなど)からの読み込み→ドメインイベントへの変換。
保証:デフォルトでは、少なくとも一度は消費者と投影者は同一でなければなりません。
ストレージの選択
書き込み:トランザクションのリレーショナル(PostgreSQL/MySQL);KV/Document-不変量が単純な場所。
読む:- KV/Redis-カードとクイックキーの読み取り。
- 検索(OpenSearch/Elasticsearch)-検索/フィルタ/ファセット;
- 列(ClickHouse/BigQuery)-レポート;
- CDN上のキャッシュ-パブリックディレクトリ/コンテンツ。
統合パターン
APIレイヤー:'コマンド'と'クエリ'の個別のエンドポイント/サービス。
Idempotency:ヘッダー/ボディの操作のキー。TTLの最近のキーの貯蔵。
サガ/オーケストレーション:タイムアウト、補償、ステップの再現性。
Backpressure-projectionプロセッサの並列性を制限します。
Observability
メトリクスを書く:p95/99コマンドの遅延、成功したトランザクションの割合、検証エラー。
メトリクスを読む:p95/99リクエスト、ヒットレートキャッシュ、検索クラスタへのロード。
投影遅延(時間とメッセージ)、DLQ率、重複排除率。
トレース:'trace_id'は→outboxコマンド→→query投影を通過します。
安全性とコンプライアンス
権利の分離:執筆および読書のための異なったスコープ/役割;最も少ない特権の原則。
PII/PCI:投影の最小化;at-rest/in-flight暗号化;マスキングだ。
監査:チーム、俳優、結果、'trace_id'を修正します。重要なドメイン(支払い、KYC)のWORMアーカイブ。
テスト
コントラクトテスト:コマンド(エラー、不変量)とクエリ(フォーマット/フィルタ)。
投影テスト:一連のイベント/CDCを提出し、最終的な読み取りモデルを確認します。
カオス/レイテンシー:投影プロセッサにレイテンシーを注入する。遅延時のUXチェック。
再生可能性:スナップショット/ログからスタンド上の投影を再構成します。
移行と進化
新しいフィールド-イベント/CDCの添加;読み取りモデルは再構築されます。
回路を再設計する場合の二重ライト;切り替えるまで古い投影を保持します。
バージョン管理:'v1'/'v2'イベントとエンドポイント、Sunset-plan。
フィーチャーフラグ:カナリアに沿った新しいクエリ/投影の導入。
アンチパターン
簡単なCRUDサービスのCQRS「ファッションのために」。
ハード同期読み書き依存性(絶縁と永続性を殺す)。
異機種混在クエリを1つの読み取りストアにミックスする。
投影は、idempotency→duplicatesとdiscrepanciesを持っていません。
回復不能な投影(リプレイ/スナップショットなし)。
ドメインの例
支払い(オンラインサービス)
書き込み: トランザクションデータベースで'Authorize'、 'Capture'、 'Refund';outboxは支払を公開します。'.
読む:- UIのためのRedisの「支払いカード」;
- 報告のためのClickHouse;
- トランザクションを検索するOpenSearch。
- クリティカルパス:承認≤ 800 ms p95;UIの一貫性の読み取り-最終的な(最大2-3秒)。
KYC(キーク)
書き込み:ステータスを開始/更新するコマンド;安全なデータベース内のPIIストレージ。
読む:PIIなしのステータスの軽量投影。必要に応じてPIIをポイント的に締めます。
セキュリティ:ステータスの読み取りとドキュメントへのアクセスのスコープが異なります。
バランスシート(iGaming/Finance)
書き込み:'UserBalance'の集計と原子の増分/減算;手術のためのidempotentキー。
読む:「クイックバランス」のキャッシュ。書き込み-書き込みからの直接読み取り(厳密な一貫性)。
佐賀:預金/結論は、失敗の場合には、イベントによって調整されます-補償。
実装チェックリスト
- 書き込みモデルの集計と不変量がハイライトされます。
- キークエリは定義され、プロジェクションはそれらのために設計されています。
- Outbox/CDCおよびidempotentプロジェクションプロセッサが設定されています。
- スナップショット/リプレイプランがあります。
- SLO:コマンド遅延、プロジェクション遅延、読み取り/書き込み可用性を別々に。
- 分離されたアクセス権とデータ暗号化が実装されています。
- DLQアラート/遅延/重複除外障害。
- テスト:契約、予測、カオス、リプレイ。
FAQ(よくある質問)
CQRSのイベントソーシングは必須ですか?
いいえ、そうではありません。通常のデータベース+outbox/CDCでビルドできます。
どのように非同期に対処するには?
明示的にUXを設計し、投影遅延を測定し、重要な操作を書き込みから読み取ることができます。
書き込みと読み取りの両方を同じサービスに保つことは可能ですか?
はい、物理的な分離は任意です;論理的な責任分担は必須です。
集計間のトランザクションはどうですか?
サガやイベントを通じて;可能であれば分散トランザクションを避けます。
[結果]
CQRSは手を解き放ちます:明確な不変量と高速で薄く信頼性の高い書き込みパス、実体化された投影からのターゲット読み取り。これにより、生産性が向上し、進化が簡素化され、一貫性、観測性、移行が規律化されている場合、システムはストレスに強くなります。