私は、ドメインが公開するユースケースを表すためのコマンドオブジェクト、およびそれらのコマンドを処理するためのコマンドハンドラーオブジェクトの使用について読んでいます。
例えば:
RegisterUserCommand
RegisterUserCommandHandler
しかし、これはRegisterUserService
とまったく同じように見えます。コマンドオブジェクトはregisterUser()
メソッドのパラメーターを表します。
そしてもちろん、メソッドのパラメーターが多すぎると、それらをラップするオブジェクトを作成することになり、そのオブジェクトはRegisterUserCommand
と同じになります。
では、なぜ同じものを表すために異なるパターンがあるのでしょうか?サービスは広く普及しており、コマンドではありません(私の経験から)。ここで私が見逃している違いは何ですか?要するに、なぜ私は他のものではなく一方を使用するのですか?
コマンドがあると、古き良きコマンドパターンの利点が得られます。
サービスが大きく、それぞれに多くの複雑なメソッドがある場合(そしてメソッドが複雑でない場合は、おそらくDDDまたはCQRSを使用するべきではありません)、各メソッドをコマンドハンドラーに移動すると、アプリケーションがより構成しやすくなり、大きなサービスからコマンド/コマンドハンドラーに直接リファクタリングする人は、これを後者のパターンの利点と見なすのが一般的です。ただし、大きなサービスを小さなサービスに分解する(例の非常に具体的なサービスで提案されている)ことで同じ利点を得ることができるため、厳密に言えば、サービスとコマンド/コマンドハンドラーの違いはありません。
これらの2つの概念はコンテキストが似ているように見えるのではないかと疑問に思うのは当然だと思います。戻って、実際に彼らが何を意図しているのかを検討する価値があるでしょう。
ドメイン駆動設計には、さまざまなタイプのサービスがあります。アプリケーションサービス(通常はUIサービス)、インフラストラクチャサービス、ドメインサービス。
ジミー・ボガードはこれらを説明するのに優れた仕事をしています
一言で言えば:
ドメインサービス
ドメインサービスの仕事は、通常、1つのエンティティには適さない機能を実行することです。さまざまな機能が必要な機能がある場合は、ドメインサービスの使用を検討してください。
エンティティ(集合/値オブジェクト)。たとえば、住宅ローンにかかる費用の見積もりを計算するには、購入者の収入/雇用の詳細が必要です。購入者の信用履歴が必要になる場合があり、最後に住宅ローンが検討されている建物に関する情報が必要になる場合があります。
pricingService.CalculateMortageEstimate(BuyerIncomingDetails bid, BuyerCreditHistory bch, BuildingOverview bo)
アプリケーションサービス
UIの一部として使用されるサービスの例。
インフラストラクチャサービス
外部リソース(メール送信者、ファイルシステム、xmlファイル、ftpなど...)と通信する傾向があるサービス
コマンドクエリの責任の分離。缶に書かれているように;以下の分離:
cQRSの使用は必ずしも適切なオプションではありませんが、私の経験では、データが複数のデータソースに分散されている場合、人々はCQRSを使用する傾向があります。
したがって、コマンドを使用して、作業ユニット(UnitOfWorkパターンと混同しないでください)を実行することを明示的に要求しています。 AddFraudRecordCommandまたはUpdateNoteCommand。
DDDサービスとCQRSコマンドの違いについて少しおさらいします。私は次のことに注意します:
Command/CommandHandlersも必要ですか?私は何を得ていますか、サービスに直接行くべきですか?
私のコマンドハンドラーの仕事は、コマンドのロジックを処理することです(コマンドは非常に具体的な要求です)。 DDDサービスには異なるジョブがありますが(ドメインサービス:複数のエンティティの機能を調整し、インフラストラクチャサービス:外部サービス(メールなど)と連携します)
多分このように考えてください:CommandHandlerジョブ–特定のコマンドを実行するコードを実行します(これには複数のサービスの使用が含まれる場合があります)。サービスジョブ–サービスの種類によって異なります。
最良の例ではありませんが、私が言おうとしていることが明らかになることを期待しています:
public class CalculateFraudProbabilityCommandHandler : CommandHandler<CalculateFraudProbabilityCommand>
{
IFraudService _fraudService;
IEmailNotifier _notifier;
ICustomerRepository _customerRepo;
public CalculateFraudProbabilityCommandHandler(ICustomerRepository customerRepo, IFraudService fraudService, IEmailNotifier notifier)
{
_fraudService = fraudService; //Domain Service
_notifier = notifier; //Infrastructure Service
_customerRepo = customerRepo; //Repository
}
//Execute Command
public void Execute(CalculateFraudProbabilityCommand command) {
Customer customer = _customerRepository.GetById(command.CustomerId);
FraudHistory fraudHistory = _fraudService.RetrieveFraudHistory(customer);
//any fraud recently? if so, let someone know!
if(fraudHistory.FraudSince(DateTime.Now.AddYear(-1)) {
_notifier.SendEmail(_fraudService.BuildFraudWarningEmail(customer, fraudHistory));
}
}
}