7

一連のルールに基づいて更新する必要のあるフィールドを持つ単純なEntityFrameworkでサポートされたドメインオブジェクトを持つシステムを設計しています-これらのルールを段階的に(アジャイルスタイルで)実装したいので、EFを使用しているので懐疑的です各ルールをドメインオブジェクトに配置する方法について。ただし、「手続き型コード」を記述したり、貧血ドメインモデルを使用したりすることは避けたいと思います。これもすべてテスト可能である必要があります。

例として、オブジェクトは次のとおりです。

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

}

「給与が100,000を超え、_eligibleForPensionがfalseの場合、_eligibleForPensionをtrueに設定する」、「_pensionがtrueの場合、_eligibleForPensionをtrueに設定する」などのルールを作成する必要があります。

そのようなルールは約20ありますが、それらをEmployeeクラスに実装するか、EmployeeRulesクラスのようなものに実装するかについてアドバイスを求めています。私の最初の考えは、「Rule」から継承するルールごとに個別のクラスを作成し、Visitorパターンを使用して、各ルールをEmployeeクラスに適用することでしたが、これを行うには、すべてのフィールドをルールに公開する必要があります。気分が悪い。Employeeクラスに各ルールを設定することも、まったく正しくないと思います。これはどのように実装されますか?

2番目の懸念は、実際のEmployeesがDBにバックアップされたEntity Frameworkエンティティであるため、これらの「エンティティ」にロジックを追加することに満足していないことです。特に、各ルールの単体テスト用にオブジェクトをモックする必要がある場合はそうです。同じオブジェクトでテストしているルールがある場合、どうすればそれらをモックできますか?

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

4

2 に答える 2

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();
            }
        }
    }
}

ここでの1つの問題は、Employeeクラスにすべてのルール実装が含まれている必要があることです。ルールは従業員年金に関連するビジネスロジックを具体化しており、それらは一緒に属しているため、これは大きな問題ではありません。

于 2013-03-26T20:20:06.437 に答える
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オブジェクトから直接ビルドしていて、すべての動作を含めたくない、または含められない場合は、それで問題ありません---しかし、それは確かに役立ちます:)

于 2013-03-27T05:20:25.753 に答える