7

私は、一連のルールに基づいて更新する必要のあるフィールドを持つ単純なEntity Frameworkバックアップドメインオブジェクトを持つシステムを設計しています。私はEFを使用しているので、各ルールをドメインオブジェクトに入れることについて懐疑的です。しかし、私は "手続き型コード"の作成と貧血ドメインモデルの使用を避けたい。このすべては、同様にテスト可能である必要があります。例として、オブジェクトがビジネスロジックでルールの形で貧血ドメインモデルを回避する方法

されています:

class Employee { 
private string Name; 
private float Salary; 
private float PensionPot; 
private bool _pension; 
private bool _eligibleForPension; 

} 

_pensionが、その後真であるならば、私は」そのような「給与が100,000以上であると_eligibleForPensionが偽であるならば、真として_eligibleForPensionを設定する」などのルールを構築する必要があり、 _eligibleForPensionをtrueに設定します。 "

約20のルールがあり、私はEmployeeクラスまたはEmployeeRulesクラスのようなものに実装する必要があるかどうかアドバイスを探していますか?私の最初の考えは、 "Rule"を継承する各ルールに対して別々のクラスを作成し、Employeeクラスに各ルールを適用することでした。多分Visitorパターンを使用しましたが、これを行うにはすべてのフィールドをルールに公開する必要があります間違っていると感じる。 Employeeクラスに各ルールを持たせることは、どちらかといえばかなり気にしません。これはどのように実装されますか?

2番目の懸案事項は、実際の従業員がDBにバックアップされたEntity Frameworkエンティティであるため、特にこれらの「エンティティ」に論理を追加する気がしないということです。特に、各ルールを単体テストするためにオブジェクトをモックする必要がある。同じオブジェクトでテストしているルールがあれば、私はどのようにそれらを嘲笑することができますか?

私はAutoMapperを使ってルールを適用する前に簡単なドメインオブジェクトに変換することを考えていましたが、フィールドの更新を自分で管理する必要がありました。これについてのアドバイスもありますか?

答えて

7

1つの方法は、ルールを内部クラスEmployeeにすることです。このアプローチの利点は、フィールドをプライベートにすることができることです。また、ルールの呼び出しは、Employeeクラス自体によって強制することができ、必要なときにそれらが常に呼び出されていることを保証する:ここ

class Employee 
{ 
    string id; 
    string name; 
    float salary; 
    float pensionPot; 
    bool pension; 
    bool eligibleForPension; 

    public void ChangeSalary(float salary) 
    { 
     this.salary = salary; 
     ApplyRules(); 
    } 

    public void MakeEligibleForPension() 
    { 
     this.eligibleForPension = true; 
     ApplyRules(); // may or may not be needed 
    } 

    void ApplyRules() 
    { 
     rules.ForEach(rule => rule.Apply(this)); 
    } 

    readonly static List<IEmployeeRule> rules; 

    static Employee() 
    { 
     rules = new List<IEmployeeRule> 
     { 
      new SalaryBasedPensionEligibilityRule() 
     }; 
    } 

    interface IEmployeeRule 
    { 
     void Apply(Employee employee); 
    } 

    class SalaryBasedPensionEligibilityRule : IEmployeeRule 
    { 
     public void Apply(Employee employee) 
     { 
      if (employee.salary > 100000 && !employee.eligibleForPension) 
      { 
       employee.MakeEligibleForPension(); 
      } 
     } 
    } 
} 

一つの問題は、Employeeクラスは、すべてのルールの実装が含まれていなければならないことです。ルールは従業員年金に関連付けられたビジネスロジックを具現しているので、これは重大な問題ではないため、それらは一緒に所属しています。

+0

役立つだろうトリック。私は自分の望むデザインである私的なフィールドの例を挙げましたが、EFはパブリックプロパティを持っていますので、Employeesクラスに直接アクセスすると内部クラスを使用する必要はありません。私は誰かがEF部品を含む質問に答えることができることを願って少し質問を開いたままにします。ありがとう! – PCurd

+0

私は少し修正されたアプローチを使ってシステムのデモモデルを作りました - 私は内部クラスとしてのルールを持っていません - それはうまく動作し、良い感じです。このシステムでは、公開されているフィールドがひどい犯罪ではないことを知っていますが、内部クラスを使用して再度そのメソッドがどのように感じられるかを調べる可能性があります。ご協力いただきありがとうございます。 – PCurd

4

通常、ビジネスルールは興味深いトピックです。集約/エンティティ不変量とビジネスルールの間には違いがあるかもしれません。ビジネスルールには外部データが必要な場合があり、集約/エンティティを変更するルールには同意しません。

ルールの仕様パターンを考える必要があります。ルールは、基本的には、それが壊れているかどうかをソートの説明とともに返します。

例では、SalaryBasedPensionEligibilityRuleは、eulerfxで使用されているように、PensionThresholdが必要な場合があります。ルールは本当にエンティティの妥当性をチェックしていないので、このルールは実際にはタスクのように見えます。

私は、ルールは決定メカニズムであり、タスクは状態を変更することをお勧めします。

あなたはおそらくあなたが状態を公開したくない場合がありますので、ここでのアドバイスのためのエンティティをお願いしたいと思います言われていること:

public class Employee 
{ 
    float salary; 
    bool eligibleForPension; 

    public bool QualifiesForPension(float pensionThreshold) 
    { 
     return salary > pensionThreshold && !eligibleForPension; 
    } 

    public void MakeEligibleForPension() 
    { 
     eligibleForPension = true; 
    } 
} 

これは、コマンド/クエリ分離アイデアをスティック。

あなたのORMのオブジェクトから直接構築しているとしたくない、またはできない場合は、すべての行動が、その後それはOKですが含ま---それは確かに、それはどうなるのようにこれが見えます:)

+0

興味深いアプローチで、「オブジェクトとの論理を維持する」というマントラに適しています。あなたは、ビジネスロジックがEmployeeオブジェクトに実装されていることになり、「このルールの条件を満たしていますか?そしてこのアップデートをトリガーしますか?」という質問をする別のオブジェクトを持つことになります。私はこれが急速に複雑になるのを見ることができますが、すべてのロジックをEmployeeオブジェクトにアタッチしています。私はそれが従業員オブジェクトの多くの条件と更新が望ましいかどうかに依存していると思います。 更新をマーシャリングする他のオブジェクトがどのようなものになるかの例を挙げることはできますか? – PCurd

+0

"更新をマーシャリングする他のオブジェクトの外観は?" ---私は本当にこのことを理解していません:) ---あなたはあなたの質問を書き直せますか? –

+0

申し訳ありません! 「employee.QualifiesForPension(100000)then employee.MakeEligibleForPension();」のような関数があるはずです。これと従業員の関係はどのように関係しますか - それは別のクラスになるでしょうか?そうであれば、どのように見えますか? – PCurd

関連する問題