How to avoid anemic domain model with business logic in the form of rules
D

2

7

I am designing a system that has a simple Entity Framework backed domain object that has fields I need to update based on a series of rules - I want to implement these rules progressively (in an agile style) and as I am using EF I am sceptical about putting each rule into the domain object. However, I want to avoid writing "procedural code" and using anemic domain models. This all needs to be testable as well.

As an example, the object is:

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

}

I need to build rules such as "if Salary is higher than 100,000 and _eligibleForPension is false then set _eligibleForPension as true" and "if _pension is true then set _eligibleForPension as true".

There are approximately 20 such rules and I am looking for advice whether they should be implemented in the Employee class or in something like an EmployeeRules class? My first thought was to create a separate class for each rule inheriting from "Rule" and then apply each rule to the Employee class, maybe using the Visitor pattern but I'd have to expose all the fields to the rules to do this so it feels wrong. Having each rule on the Employee class though doesn't feel quite right either. How would this be implemented?

The second concern is that the actual Employees are Entity Framework entities backed to the DB so I don't feel happy adding logic to these "Entities" - especially when I need to mock the objects for unit testing each rule. How could I mock them if they have the rules I'm testing on the same object?

I have been thinking of using AutoMapper to convert to a simpler domain object before applying rules but then need to manage the updates to the fields myself. Any advice on this too?

Duarte answered 26/3, 2013 at 19:53 Comment(0)
H
7

One approach is to make the rules inner classes of Employee. The benefit of this approach is that the fields can remain private. Also, the invocation of the rules can be enforced by the Employee class itself, ensuring that they are always invoked when needed:

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 here is that the Employee class has to contain all rule implementations. This isn't a major problem since the rules embody business logic associated with employee pensions and so they do belong together.

Hairdo answered 26/3, 2013 at 20:20 Comment(2)
This looks like it would do the trick. Although I gave an example with private fields, which is my desired design, EF has public properties so I wouldn't have to use inner classes if I accessed the Employees class directly. I'm going to leave the question open for a bit in hopes someone can answer question including the EF parts. Thanks!Duarte
I built a demo model of the system using a slightly modified approach - I don't have the rules as inner classes - and it works and feels good. I know for this system that having the fields public isn't a terrible crime but I might try again using inner classes to see how it feels. Thanks for your help.Duarte
L
4

Business rules are usually an interesting topic. There may certainly be a difference between an aggregate / entity invariant and a business rule. Business rules may need external data and I wouldn't agree with a rule changing an aggregate / entity.

You should think specification pattern for rules. The rule should basically just return whether it was broken or not with possibly a description of sorts.

In your example SalaryBasedPensionEligibilityRule, as used by eulerfx, may need some PensionThreshold. This rule really does look more like a task since the rule really isn't checking any validity of the entity.

So I would suggest that rules are a decision mechanism and tasks are for changing the state.

That being said you probably want to ask the entity for advice here since you may not want to expose the state:

public class Employee
{
    float salary;
    bool eligibleForPension;

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

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

This sticks with the command/query separation idea.

If you are building directly from your ORM objects and do not want to, or cannot, include all the behaviour then that is OK --- but it certainly would help :)

Lutenist answered 27/3, 2013 at 5:20 Comment(5)
An interesting approach and fits better with the "keep the logic with the object" mantra. You'd end up with each piece of business logic being implemented on the Employee object and then having another object responsible for asking "do you meet the conditions of this rule? Then trigger this update"? I can see this getting rapidly complex, but also keeping all the logic attached to the Employee object. I guess it depends whether having lots of conditions and updates on the Employee object is desirable. Can you give an example what the other object marshalling the updates would look like?Duarte
"Can you give an example what the other object marshalling the updates would look like?" --- I really don't understand this :) --- Can you please restate your question?Lutenist
Sorry! There would have to be a function somewhere that did "if employee.QualifiesForPension(100000) then employee.MakeEligibleForPension();" how would the relationship between this and Employee work - would it be a separate class and if so, what would it look like?Duarte
Ah, OK :) --- That would be the 'Task' bit I was on about (operation script) in a separate class. Got this link: my.safaribooksonline.com/book/web-development/9780321669636/… where it is defined quite well: Operation Script [POEAA] Contains application logic that directs the activities of entities (i.e., objects) in a Domain Model [POEAA]. Each script typically fulfills a use case in an application domain. Operation Scripts are different from Transaction Scripts [POEAA] in that they delegate most of their work to domain objects.Lutenist
This sounds interesting, and for a larger system I think there is value to be had here but I've decided for this system, on this scale that eulerfx's approach is more practical for me. Thanks a lot though, I've learnt a new trick!Duarte

© 2022 - 2024 — McMap. All rights reserved.