Command自体のメソッドを処理するのではなく、Handle()でクラスCommandHandlerを分離する理由


13

次のようなS#arpアーキテクチャを使用して実装されたCQRSパターンの一部があります。

public class MyCommand
{
    public CustomerId { get; set; }

    // some other fields
}

public class MyCommandHandler<MyCommand> : ICommandHandler<MyCommand, CommandResult>
{
    Handle(MyCommand command)
    {
        // some code for saving Customer entity

        return CommandResult.Success;
    }
}

なぜCommandデータ処理メソッドの両方を含むクラスを持たないのでしょうか?コマンド処理ロジックをコマンドプロパティとは別にテストする必要がある場合、一種のテスト容易性の利点ですか?または、さまざまな実装で1つのコマンドを処理する必要がある、頻繁なビジネス要件ICommandHandler<MyCommand, CommandResult>ですか?


:私は、同じ質問、価値が探していたblogs.cuttingedge.it/steven/posts/2011/...
rdhaundiyal

回答:


14

面白いことに、この質問は、私が取り組んでいたコミュニケーションライブラリについて、エンジニアの1人とまったく同じ会話を思い出したことです。

コマンドの代わりに、Requestクラスがあり、その後RequestHandlerがありました。デザインは、あなたが説明しているものと非常に似ていました。あなたが持っている混乱の一部は、英語の「コマンド」という言葉を見て、即座に「動詞、行動...など」と考えることだと思います。

しかし、この設計では、コマンド(またはリクエスト)を文字と考えてください。または、郵便サービスとは何かを知らない人のために、電子メールを考えてください。それは単にコンテンツであり、そのコンテンツがどのように行動されるべきかから切り離されています。

なぜこれをするのですか?Command Patternのほとんどの単純なケースでは、理由はなく、このクラスに直接作業を実行させることができます。ただし、アクション/コマンド/リクエストがある程度の距離を移動する必要がある場合、設計のようにデカップリングを行うことは理にかなっています。たとえば、接続、ソケット、パイプ、またはドメインとインフラストラクチャ間。または、アーキテクチャ内でコマンドを永続化する必要があるかもしれません(たとえば、コマンドハンドラーは一度に1つのコマンドを実行できます。一部のシステムイベントにより、200コマンドが到着し、最初の40プロセスがシャットダウンした後)。その場合、単純なメッセージのみのクラスを使用すると、メッセージ部分だけをJSON / XML / binary / whateverにシリアル化し、コマンドハンドラーで処理できるようになるまでパイプラインに渡すことが非常に簡単になります。

CommandHandlerからCommandを切り離すことのもう1つの利点は、並列継承階層のオプションが用意されたことです。たとえば、すべてのコマンドは、シリアル化をサポートする基本コマンドクラスから派生できます。また、20個のコマンドハンドラのうち4個が非常に類似していて、来たハンドラの基本クラスから派生できる可能性があります。1つのクラスでデータとコマンドを処理する場合、このタイプの関係はすぐに制御不能になります。

分離の別の例は、コマンドが非常に小さな入力(2つの整数と文字列)を必要とするが、その処理ロジックがデータを中間メンバー変数に格納するのに十分複雑な場合です。50個のコマンドをキューに入れる場合、その中間ストレージすべてにメモリを割り当てたくないため、CommandHandlerからCommandを分離します。これで、50個の軽量データ構造をキューに入れ、より複雑なデータストレージが、コマンドを処理しているCommandHandlerによって1回のみ(または、N個のハンドラーがある場合はN回)割り当てられます。


ポイントは、このコンテキストでは、コマンド/リクエストはリモート/永続化などではないということです。直接処理されます。そして、2つの分離が継承にどのように役立つかわかりません。実際には難しくなります。最後の段落も一種のミスです。オブジェクトの作成は高価な操作ではなく、50コマンドは無視できる数です。
陶酔14年

@ユーフォリック:コンテキストがどのようにわかるのですか?S#arp Architectureが特別なものでない限り、私が見るのは2、3のクラス宣言だけであり、アプリケーションの他の部分でどのように使用されるのかわかりません。私が50のように選んだ数値が気に入らない場合は、1秒あたり50のような値を選択してください。それで十分でない場合は、1秒あたり1000を選択します。私は例を提供しようとしていました。それとも、この文脈では彼はそれほど多くのコマンドを持っているとは思わないのですか?
DXM 14年

たとえば、正確な構造はweblogs.asp.net/shijuvarghese/archive/2011/10/18/…にあります。そして、どこにもあなたの言ったことはありません。また、速度については、プロファイリングせずに「パフォーマンス」引数を使用したことが問題です。そのようなスループットの要件がある場合は、汎用アーキテクチャを使用するのではなく、より特殊なものを作成します。
陶酔14年

1
これが最後のポイントであったかどうかを見てみましょう:OPは例を求めました、たとえば、最初にシンプルな方法を設計してアプリケーションが動作することを言ってから、スケールアップしてコマンドパターンを使用する場所を拡張してから、ライブになり、サーバーと通信する10,000台のマシンがあり、サーバーはまだ元のアーキテクチャを使用しているので、問題をプロファイリングして特定し、プロファイリング後にのみコマンドデータをコマンド処理から分離できます。私がすべてを答えに含めたなら、本当にあなたが幸せになるでしょうか?彼は例を求めて、私は彼に一つを与えました。
DXM 14年

...だから私はあなたが投稿したブログの投稿を一目見ただけで、私が書いたものと一致しているようです。ブログでは、彼は基本的に別のパイプ、ソケット、メッセージキュー、esbなどのコマンドバスに言及しているようです。
DXM14年

2

通常のコマンドパターンは、単一のクラスでデータと動作を保持することです。この種の「コマンド/ハンドラパターン」はわずかに異なります。通常のパターンと比較した唯一の利点は、コマンドがフレームワークに依存しないという追加の利点です。たとえば、コマンドにはDBアクセスが必要な場合があるため、何らかのDBコンテキストまたはセッションが必要です。つまり、フレームワークに依存しています。ただし、このコマンドはドメインの一部である可能性があるため、Dependency Inversion Principleに従ってフレームワークに依存することは望ましくありません。入力パラメータと出力パラメータを動作から分離し、それらを結び付けるディスパッチャを用意することでこれを修正できます。

一方、コマンドの継承と構成の両方の利点を失うことになります。それが本当の力だと思います。

また、マイナーなピック。名前にCommandが含まれているからといって、CQRSの一部にはなりません。それはもっと基本的なことです。この種の構造は、コマンドとしてもクエリとしても同時に使用できます。


リンクweblogs.asp.net/shijuvarghese/archive/2011/10/18/を見ましたあなたは指摘しましたが、私が持っているS#arp Archコードにはバスの兆候は見られません。だから、私の場合のそのような分離は、クラスを広め、ロジックを飛ばすだけです。
rgripper


うーん、指摘してくれてありがとう。コードがICommandProcessorをIOCで処理し、CommandProcessor(コマンドハンドラー用のIOCを作成している)に解決されているため、私のケースはさらに悪化しています。また、このプロジェクトでは、1人の指揮官に対する複数のハドラーのビジネスケースはないようです。
rgripper 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.