2016-06-21 16 views
2

BobさんのブログとTSS id DEADの記事を読んだ後、もう少しテストをしたいと思っていました。DDD - ApplicationServices単体テスト

私のアプリケーションはレイヤーを使用しています:ApplicationService - > Model - > Support。

アプリケーションサービスはユースケースを定義します。これは少し手続き型のコードです(loadX、X.doSomething、saveXを呼び出します)。場合によっては、appServiceクラスには多くの外部依存関係が必要です。例えば

、Iは「ワークフロー」の終了時に使用されるオブジェクト「D」を有します。だから、それを使って作業するとき、私はこのオブジェクトにリンクされているオブジェクト "A"/"B"/"C"に関するいくつかのルールをチェックする必要があります。 3つのエンティティがそれぞれ独自のリポジトリに関連付けられており、非常に特殊なケースでは、別のルールを深く(「AA」==別のリポジトリのように)チェックする必要があります。

私appServiceは8〜10の依存関係(5つのリポジトリ+いくつかのビジネス・サービス間のエンティティ)のようなものを持っています。私の "義務"ではないテスト。私はあまりにも多くのサービスを嘲笑するとき、勇気が足りません。各サービスに3つのメソッドがありますが、私のappServiceに必要なメソッドが1つだけの場合は、すべてのメソッドを模擬したり模擬したりする方法を知っている必要があります(無駄なメソッドの2/3をモック/スタブします)。

私は多くの問題があると思います。私の最初のものはあまりにも多くの依存関係です。私は依存関係を分割してカーペットの下に隠したくないだけです。悪い境界、あまりにも多くの分離など

article (uncle bob)を読んだ後、私は間違っていると思っていました。おそらくappServiceは、必要なものとのインタフェースを表現/宣言する必要があります。しかし、それをどのように名付けますか?それを分割する方法は?私は関連するサービスを一緒に置くことはお勧めできません。ファサードのようなものを書くとテストがより簡単になります(私のクラスは、これらの外部サービスと結びついていて、どれが正確に分かっているのか分かります)。

  • どのようにあなたのappServicesをテストするのですか?
  • 注入されたサービス(「ファサード」のようなものか、より多くのビジネスや何か他のもの)を分割することは良いアプローチですか?

おかげ

アップデート1(2016年6月23日):exempleのために、ここではアプリケーション・サービス・クラスのための DPAppServiceその依存関係のリスト:非常に迷惑、

DPRepository dpRepository; 
RechercheFournisseurQueryService rechercheFournisseur; 
RechercheFactureQueryService rechercheFacture; 
DateService dateService; 
SFRepository sfRepository; 
CommandeRepository commandeRepository; 
RepartitionBudgetRepository repartRepo; 
GenerateurRepartitionsDPService genRepartDpService; 
SaisieRepartitionsSurDpQueryService    saisieRepartitionsSurDpQueryService; 
ImputationBudgetaireService imputationBudgetaireService; 
NomenclatureService nomenclatureService; 
ComptaGeneraleService comptaGeneraleService; 
OperationInfoService operationInfoService; 
DemandePaiementCalculateurService dpCalculateurService; 
CodeMarcheAnnualiseRepository codeMarcheAnnualiseRepository; 
DemandePaiementLigneFactory dpLigneFactory; 
EcritureRepository ecritureRepository; 
BrouillardNonViseRepository brouillardNonViseRepository; 

かなり怖いですコンストラクタ注入を使用して注入する。リポジトリにはいくつかのリポジトリがあります(あまりにも多く、私は思っており、DPサービスはリポジトリによって公開されているメソッドのサブセットしか必要としません)、いくつかのサービス(現時点では自分のドメインで実際には保持できない計算)

ドメインについてもう少し説明する必要があります。 このクラスは、 "DP"コンセプトに付けられたすべてのユースケース(20のメソッド、いくつかのメソッドが別のメソッドと異なるだけのもの)を担当します。コンセプトは、「ワークフロー」(ここでは緩い使用される用語)の終わりにされています

  1. 最初のユーザーは「EJ」
  2. を作成し、その後、彼は「SF」
  3. が、その後、彼は「骨折」を作成し、作成します
  4. 最終的に、プリビジュアルオブジェクトをバインドするDPを作成するので、プリビジュアルオブジェクトの使用状況を確認/ロードする必要があります。 DPは本当にすべてのプリビジュアルオブジェクトを保持することはできません(ビットが多すぎます)。

アイデア:

  • 私は、依存関係の数を制限するために(サブ機能ごとに、ネストされたパッケージを作成する)ユースケースを分割することができます。
  • 私は、モデルをリファクタリングすることができます
  • それが必要とインタフェースを

どれ長所/短所/アイデア(私は叔父ボブからのTDD /少しアーキテクチャについての私のlectrues後にこのアイデアを調査していた)を提供することができますクラス?

あなたの時間/お答えいただきありがとうございます。

+1

おそらく多くの依存関係を持つアプリケーションサービスそれが提供するサービスが非常に粘着的ではなく、おそらくあまりにも多くのことをしていることを意味します。あなたは、そのサービスのインターフェースと少なくともあなたが正しい方向を示すようにするために必要な依存関係の例を挙げるべきです。もう1つは、サービスの数を減らすためにできることは、いくつかの依存関係をグループ化し、当初はアプリケーションサービスに直接関わっていたいくつかの操作を運ぶ新しい結束概念を見つけることだと思います。 – plalx

+0

+1、このサービスは間違いなく膨らんだようです。あなたのビジネスドメインに高レベルの「ワークフロー」を形成するアクティビティがいくつかあるため、一般的にそのすべてが同じアプリケーションサービス内にある必要はありません。アプリケーションサービスは通常、集約に沿っています。 – guillaume31

答えて

0

過去に何度もこの問題が発生しました。ここでは、ワークフローのようなロジックがあり、結果として多くの依存関係があります。

これに取り組む方法は、パイプライン/責任連鎖パターンなどのパターンを使用することです。つまり、10以上の依存関係を持つワークフローがある場合、ワークフローの各段階を個別のドメインサービスに分割します。以下のサービスは、銀行の詳細をチェックするためのワークフローに存在します有効です。いくつかのドメインサービスがまたは実行されない場合があること

using System; 

public interface IService 
{ 
    bool CanExecute(
     IContext subject); 


    Context Execute(
     IContext subject); 
} 

注:

initial-validation 
check-name 
check-account 
check-for-fraudulent-behaviour 
event-publication 
result-builder 
save-result 

これらのドメインの各サービスは、次のインターフェイスを実装します例えば初期検証が失敗した場合、名前を確認する必要はありません。しかし、結果が保存されても、結果は常に成功か失敗かを問わず実行されます。

したがって、10以上の依存関係が注入される代わりに、テストしようとしているワークフローにドメインサービスのコレクション(パイプライン)が注入されます。

このパターンは、ワークフローロジックを作成するためにコードをリンクする機能を持つため、優れていますが、コードのテスト容易性や拡張性などははるかに優れています。

各ドメインサービスの実装は、単体レベルで簡単にテストすることができ、それぞれのリポジトリ/外部サービスなどの独自の依存関係を持つ可能性があります。ワークフロー自体は、あなたが望むパイプラインの振る舞いを模擬する必要があります。これは比較的単純です。

パイプラインのビルドコードを再利用できるように、パイプラインのテストを支援するパイプラインビルダーのようなものがよくあります。

最後に、各ドメインサービス実装で使用するContextオブジェクトは、すべてのドメインサービスデータ構造をリンクするものです。

public interface IContext 
    { 
     InitialValidation.IResult InitialValidationResult { get; } 
     CheckName.IResult CheckNameResult { get; } 
     CheckAccount.IResult CheckAccountResult { get; } 
     CheckFraud.IResult CheckFraudResult { get; } 
     EventPublish.IResult EventPublishResult { get; } 
     ResultBuild.IResult ResultBuildResult { get; } 
     Save.IResult SaveResult { get; } 
} 

ロングったらしい返事をするが、そのハードはわずか数段落をで記述するために:コンテキストで実行されている各ドメインサービスの各結果は、例えば以下を参照してください。簡単に言えば、ワークフローを順序付けられたドメインサービスの集合体に分割します。これにより、依存関係の構造が単純化され、コードのテストが容易になります。各サービスは、3つの方法がありますが、私のappServiceは一つだけが必要な場合

+4

あなたのドメインは貧血に苦しんでいるようです。ほとんどのルールは、サービスではなく集約でカプセル化する必要があります。 – plalx

+0

私たちの集約は、ビジネスルール、不変条件、およびDBCチェックの多くをカプセル化していません。ステップバイステップ後のワークフローロジックは、ドメインサービスにより適していることがわかりました。ワークフローは、ドメインサービス内のロジックをリンクします。 – bstack

1

は、その後、私はモックやモック/スタブすべてのメソッドを(そしてモック/無用方法の2/3をスタブ)にどの方法を知る必要があります。

うん、これはおなじみのコードです。または他の方法 - あなたは働いているテストを持っているが、テストをコンパイルし続けるために別のメソッドをスタブするためにそれらに戻って行く必要があります....

記事を読んだ後叔父さんのボブ)、私は考えていた:私はそれを間違っている。おそらくappServiceは、必要なものとのインタフェースを表現/宣言する必要があります。

はい。アプリケーションサービスは、サービスプロバイダーインターフェイスのいくつかの特質を宣言しています。これは、実行する必要があるコンテキストを記述する実装にとらわれない方法を提供します。

これは役立つ場合、これはリポジトリまたはドメインサービスインターフェイスのあなたのドメインモデル

しかし、どのように名前を付けますか?

命名物事を吸う:

  • コンテキスト
  • クライアント
  • コネクタ
  • ゲートウェイ
  • シーム

どのトンそれを分割する?私は関連するサービスを一緒に置くことはお勧めできません。

私は集計デザインのように考え始めました。これは私が意味するところです。私たちは通常、私たちのモデルの中のエンティティの構造を想像することによって集合体を考え始めます。しかし、構造は本当に静的なものです。ドメインモデルで重要なのは、2つの状態のビットが構造によって接続されているのではなく、それらの状態の変化がビジネスルールによって結びついているということではありません。

これはアプリケーションサービスレベルで何が翻訳されますか?つまり、アプリケーションサービスが複数のユースケースをサポートしている場合、そのユースケースに共通することは何ですか? 「そのように実装するのは簡単だった」以上の動機があるはずです。私はここに自分自身で答えはありませんが、質問するのが正しい質問の方向にあると思います。

どのようにあなたのappServicesをテストしますか?(ユニットまたは統合テストのみ?)

どちらも理想的です。アプリケーションサービスが稼働するモジュールには、テスト自体がサービスプロバイダとして機能するテストがあります。インテグレーションチェックは、サービスを提供するための多数のモジュールを結び付け、アセンブリに対して独立した一連のテストを実行します。

注入されたサービス(「ファサード」のようなものか、ビジネスや何か他のもの)を分割することは良いアプローチですか?

はい、費用はかかりません。ファサード自体は、実際に何が起こっているかを文書化するのに最適です。しかし、各ファサードには少なくとも1つの実装が必要であり、より多くの可能性があります(テストスタブなど)。あなたはどこかのコストを支払うつもりです。モジュールの複雑な配線、サービスの実装が実際にどこにあるのか把握し、名前クラス/パッケージ/モジュールを見つけようとしているので、すべてをまっすぐに保つことができます。