web-dev-qa-db-ja.com

貧血ドメインモデルの回避-実際の例

私は貧血ドメインモデルと、それらがおそらくアンチパターンである理由を理解しようとしています。

これが実際の例です。

名前、性別、ユーザー名など、たくさんのプロパティを持つEmployeeクラスがあります

public class Employee
{
    public string Name { get; set; }
    public string Gender { get; set; }
    public string Username { get; set; }
    // Etc.. mostly getters and setters
}

次に、電話の着信とWebサイトの問い合わせ(「リード」と呼ばれる)を営業スタッフ間で均等にローテーションするシステムがあります。このシステムは、ラウンドロビンの問い合わせ、休日のチェック、従業員の好みなどを伴うため、非常に複雑です。したがって、このシステムは現在、EmployeeLeadRotationServiceというサービスに分離されています。

public class EmployeeLeadRotationService : IEmployeeLeadRotationService
{
     private IEmployeeRepository _employeeRepository;
     // ...plus lots of other injected repositories and services

     public void SelectEmployee(ILead lead)
     {
         // Etc. lots of complex logic
     }
}

次に、ウェブサイトのお問い合わせフォームの裏側に次のようなコードがあります。

public void SubmitForm()
{
    var lead = CreateLeadFromFormInput();

    var selectedEmployee = Kernel.Get<IEmployeeLeadRotationService>()
                                 .SelectEmployee(lead);

    Response.Write(employee.Name + " will handle your enquiry. Thanks.");
}

このアプローチではあまり問題は発生しませんが、貧血ドメインモデルであるため、これは叫び声を上げて実行する必要があるものと思われます。

しかし、私にとっては、リードローテーションサービスのロジックがどこに行くべきかは明確ではありません。リードするべきですか?それは従業員に行くべきですか?

ローテーションサービスに必要なすべての注入されたリポジトリなどはどうですか?従業員を扱うときはほとんどの場合、これらのリポジトリは必要ないので、どのように従業員に注入されますか?

76
cbp

この場合、これは貧血ドメインモデルを構成しません。貧血ドメインモデルは 特にオブジェクトの検証と変換について です。したがって、この例は、外部関数が実際に従業員の状態を変更したり、詳細を更新したりした場合です。

この場合に起こっていることは、あなたがすべての従業員を連れて行き、彼らの情報に基づいてそのうちの1人を選択しているということです。他の人を調べて、それが見つけたものに関して決定を下す別のオブジェクトを持つことは問題ありません。オブジェクトをある状態から別の状態に遷移させるために使用されるオブジェクトを持つことはできません。

あなたの場合の貧血ドメインモデルの例は、外部メソッドを持つことです

updateHours(Employee emp) // updates the working hours for the employee

これは、Employeeオブジェクトを受け取り、その週の労働時間を更新し、時間が特定の制限を超えた場合にフラグが立てられるようにします。これに伴う問題は、Employeeオブジェクトしかない場合、正しい制約内でそれらの時間を変更する方法についての知識がないことです。この場合、これに対処する方法は、updateHoursメソッドをEmployeeクラスに移動することです。これが、貧血ドメインモデルのアンチパターンの核心です。

54
radman

ここはあなたのデザインは素晴らしいと思います。ご存知のように、貧血ドメインモデルのアンチパターンは、ドメインオブジェクトにコード化された動作を回避する傾向に対する反発です。ただし、逆に言えば、ドメインオブジェクトに関連するallの動作をそのオブジェクトでカプセル化する必要があるという意味ではありません。

経験則として、ドメインオブジェクトに本質的に関連付けられ、1つのドメインオブジェクトインスタンスに関して完全に定義されている動作をドメインオブジェクトに含めることができます。それ以外の場合は、責任を明確に保つために、これまでと同じように外部の共同作業者/サービスに配置するのが最善です。

30
mdma

それはすべてあなたの頭の中にあります-ローテーションサービスをドメインモデルの一部と見なすと、問題は解決します。

ローテーションは多くの従業員に関する情報を保持する必要があるため、リードにも単一の従業員オブジェクトにも属しません。それ自体がドメインオブジェクトであるに値します。

「RotationService」の名前を「Organization.UserSupportDepartment」のような名前に変更するだけで、わかりやすくなります。

14
ima

ドメインモデルに役割と物事のみが含まれ、動作としてのアクティビティが含まれていない場合、それは貧血です。ただし、オブジェクトではなくモデルに関する動作について話している-)。私は別の答えでそれらの違いについて話します... https://stackoverflow.com/a/31780937/116442

あなたの質問から、あなたは私の最初の2つのドメイン分析モデリングルールを破ります:-

  1. (記録された)アクティビティとしてモデル化された動作は、ドメインモデルの中心です。最初にそれらを追加します。
  2. ドメインアクティビティをメソッドではなくクラスとしてモデル化します。

モデルにアクティビティ「照会」を追加します。これにより、モデルには動作があり、外部コントローラーやスクリプトなしでオブジェクトのグループとして結合して機能できます。

EnquiryHandlerModel

0
aryeh