Avoiding anomalous domain model using business logic in the form of rules

I am developing a system that has a simple Entity Framework object that supports a domain that has fields that I need to update based on a number of rules. I want to gradually implement these rules (in a flexible style) and, since I use EF, I am skeptical about putting each rule in a domain object. However, I want to avoid writing “procedural code” and using anemic domain models. All this also needs to be checked.

As an example, an object:

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

I need to create rules such as "if the salary is above 100,000, and _eligibleForPension is false, then set _eligibleForPension as true" and "if _pension is true, then set _eligibleForPension as true".

There are about 20 of these rules, and I'm looking for advice on whether to implement them in the Employee class or in something like the EmployeeRules class? My first thought was to create a separate class for each rule that inherits from the Rule, and then apply each rule to the Employee class, possibly using the Visitor template, but I would have to expose all the fields to the rules to do this so that he feels wrong. The presence of each rule in the Employee class, although not entirely correct. How will this be implemented?

The second problem is that the actual employees are Entity Framework entities supported by the database, so I don’t feel happy adding logic to these “objects” - especially when I need to mock objects for unit testing each rule. How could I mock them if they have rules that I test on the same object?

I thought about using AutoMapper to convert to a simpler domain object before applying the rules, but then I need to manage updates by fields myself. Any advice on this?

+7
source share
2 answers

One approach is to make the rules inner classes of Employee . The advantage of this approach is that the fields can remain closed. In addition, the rules can be invoked by the Employee class itself, ensuring that they always invoke when necessary:

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

One problem is that the Employee class must contain all rule implementations. This is not a serious problem because the rules embody the business logic associated with employee pensions and therefore belong to each other.

+7
source

Business rules are usually an interesting topic. Of course, there may be a difference between an aggregate / entity invariant and a business rule. Business rules may require external data, and I would not agree with a rule that modifies an aggregate / object.

You should consider a specification template for rules. A rule should basically simply return whether it was broken or not, perhaps with a description of the genus.

In your example, the SalaryBasedPensionEligibilityRule used by eulerfx might need PensionThreshold . This rule really looks more like a task, because the rule really does not check the validity of the object.

Therefore, I would suggest that rules are a decision-making mechanism and tasks for changing state.

Having said that, you probably want to ask the entity about it, because you can not show the state:

 public class Employee { float salary; bool eligibleForPension; public bool QualifiesForPension(float pensionThreshold) { return salary > pensionThreshold && !eligibleForPension; } public void MakeEligibleForPension() { eligibleForPension = true; } } 

This is due to the idea of ​​separating commands / requests.

If you build directly from ORM objects and don’t want or cannot include all the behavior, then this is fine --- but it would certainly help :)

+4
source

All Articles