ドメインロジックとパイプラインを持つCQRSハンドラ(C#) - Derek Comartinのビデオによる説明
Command Query Responsibility Segregation(CQRS)は、.NETアプリケーションにおける強力なデザインパターンで、読み取り操作と書き込み操作の明確な分離を維持するのに役立ちます。 このように分けることで、より優れたスケーラビリティ、テスト容易性、複雑なビジネスロジックの制御が可能になります。 しかし、アプリケーションが成長するにつれて、C#のCQRSハンドラはロジックで肥大化し、保守やテストが難しくなる可能性があります。
Clean Up Bloated CQRS Handlers with Domain Logic & Pipelines" という彼の洞察に満ちたビデオの中で、Derek Comartin は、ロジックをドメイン・モデルにシフトし、コマンド・ロジックを段階的に管理するパイプラインを構造化することによって、そのような CQRS コマンド・ハンドラをクリーンアップする方法について説明しています。この記事では、Derekのアプローチを詳しく調べ、C#でより保守性の高いCQRS実装を構築する方法を学びます。
肥大化したハンドラの問題点
C#でCQRSハンドラを開くと、めちゃくちゃになっている。 データ検証、認可、ビジネスロジック、状態遷移、イベントパブリッシング、ロギングなど、すべてが絡み合っています。
彼は、出荷を発送するためのコマンド・オブジェクトを使って、このことを説明している。 担当者の責任
データ・ストアにアクセスして出荷をロードする。
出荷ステータスの確認
ステータスの更新(データの更新)。
変更をデータアクセスレイヤーに戻して保存します。
メールを送信する
- ドメインイベントの公開
このすべてが1か所で実現します。 これは、懸念を分離し、パフォーマンスと保守性を向上させることを目的とするCQRSパターンの意図に反する。
ドメインロジックをデータモデルに移動する
デレクの最初のステップは、検証と状態遷移のロジックをデータモデルに移すことです。 彼は、Shipment クラスの中に Dispatch() メソッドを作成します。 これが、現在ドメインロジックが存在する場所です。
ハンドラで出荷ステータスを手動でチェックする代わりに、ロジックはこのメソッドにカプセル化され、ディスパッチがトリガーされるあらゆる場所でデータの整合性と一貫した動作を保証します。 これは、CQRSベースのアプリケーションにクリーンなアーキテクチャを実装するための鍵となります。
例えば、shipment.Dispatch()を呼び出す場所は、すべての検証と状態遷移を自動的に実行します。 これはCQRSのデザインパターンに沿っており、ハンドラとドメインロジックの明確な分離を維持するのに役立ちます。
ロジックを一元化する価値
デレクは、この種の変更は不必要な抽象化を加えることではないと指摘する。 その代わり、アプリケーションコードのさまざまな部分で使用されるロジックを一元化することが重要です。 複数のコマンドハンドラが出荷をディスパッチする必要がある場合、このカスタムロジックはドメインモデルの内部で1つの場所に置く必要があります。
これにより、データモデルがより堅牢になり、CQRSハンドラのC#実装がよりシンプルで保守しやすくなります。
パイプラインパターンの紹介
コマンド・ハンドラをさらにきれいにするために、デレクはパイプライン・パターンを導入します。 この構造は、コマンドを小さな単一目的のステップのシーケンスとして処理し、それぞれがコンテキスト・オブジェクトを取り、次のステップを呼び出します。
これは概念的に.NET Coreミドルウェアに似ており、各ステップはフローの特定の部分に焦点を当てています:
出荷の取得(すなわち、データの読み取り)
ディスパッチ(書き込み操作の実行)
出版イベント
- データストアへの保存
これらのステップでは、パイプラインを流れる共有コマンドオブジェクトを使用します。これにより、コマンドとクエリの責任分離をすっきりとモジュール化して実装することができます。
パイプラインのサンプル実装
彼のサンプル実装では、Derekは次のようなステップでパイプラインを構成しています:
出荷の読み込み - リポジトリを使用してデータアクセスレイヤーからデータを取り出します。
発送物の発送 - Dispatch()メソッドを呼び出してドメインロジックを適用します。
ドメインイベントを追加する - コンテキストに "ShipmentDispatched "イベントをアタッチする。
イベントの発行 - イベントをディスパッチして外部システムに通知します。
- 変更の保存 - 更新をデータストアに永続化します。
各ステップはコマンドロジックの異なる部分を表し、データの検証を強化し、責任を分離します。
Derekはまた、電子メール通知は現在、ドメインイベントに反応することによって別々に処理されることを指摘している。 これは、イベントソーシングの原則に沿い、最終的な一貫性を促進します。
テストと保守性の利点
このパターンの最大の利点の1つは、テスト容易性です。 大規模なコマンドハンドラでは、複数の依存関係(リポジトリ、メールサービス、ロガーなど)を持つことがあります。 しかし、ハンドラーをパイプラインのステップに分割すると、それぞれのステップで必要な依存関係は2、3個になります。
このモジュール式アプローチにより、依存性注入を使用して、必要に応じてフェイクやモックを使用し、個々のステップを簡単にテストすることができます。 例えば、Dispatch()を呼び出すステップをテストする場合、メールサービスやイベントパブリッシャーをモックする必要はありません。
この懸念事項の分離は、責任分離CQRSパターンに従っており、読み取りと書き込みのモデルをよりすっきりさせ、より焦点を絞ったものにします。
コンポーザビリティと再利用性
パイプライン・アプローチのもう一つの利点は、コンポーザブルであることです。 Outboxパターンのようなものを使用すると、書き込みモデルが永続化された後にのみイベントが発行されるようにすることができます。 一貫性と納期の保証が重要なCQRSの実装では、このレベルの管理が不可欠です。
また、異なるCQRSハンドラ間でステップを共有することもできます。たとえば、一般的な "SaveChanges "ステップや "ValidateRequest "ステップなどです。
コマンドとクエリの処理をサポートするMediatRライブラリのようなツールを使用すると、IServiceCollectionサービスを使用して、.NET Coreアプリケーションに依存性注入を介してこれらの手順を登録することもできます。
このシステムをセットアップするには、Visual Studioのパッケージ・マネージャ・コンソールを使ってInstall-Package MediatRを実行します。
トレードオフ
デレクは、このようなアプローチに伴う複雑さからも逃げません。 パイプラインには間接性があり、コールスタックを見ると迷路を進むような気分になります。
しかし、複雑なビジネスロジックの場合は、トレードオフの価値があることがよくあります。 ハンドラに10以上の依存関係と数百行のロジックがある場合、CQRSは開発者がこれらのフローをより適切に構造化し、維持することを可能にします。
いつリファクタリングすべきかについての最終的な考え
デレクは最後に、自分のC#ハンドラの実装が本当に肥大化しているかどうかを注意深く検討するよう、視聴者に注意を促します。 すべてのシナリオにパイプラインが必要なわけではありません。彼のゴールは可能性を示すことであり、自身のCQRS実装を評価し、そのようなパターンが役に立つかどうかを判断するのは開発者次第である。
特にCQRSウェブ・アプリケーションでは、懸念事項の分離が一貫性の維持、コードのモジュール化、読み取りと書き込み操作の管理に役立つと思われる部分を、開発者に見てもらうよう勧めている。
結論
Derek Comartin 氏の video では、ドメイン ロジックのカプセル化とパイプラインを使用して CQRS ハンドラをクリーンアップする実践的なガイドを提供しています。 このアプローチは、コードの肥大化の問題に対処し、データの整合性を促進し、アプリケーションコードを個別のモデルに分解することによって保守性を高めるのに役立ちます。
従業員データ、製品詳細、新規ユーザーコマンドのいずれを扱う場合でも、パイプラインとドメイン駆動設計によるCQRSパターンを適用することで、コードベースがよりスケーラブルで、テスト可能で、堅牢になります。
データ転送オブジェクト、個別のモデルを使用し、読み取りロジックと書き込みロジックの明確な分離を維持することで、.NETアプリケーションの構造が改善され、長期的な進化が容易になります。



