OJ Develops

Thoughts on software development. .NET | C# | Azure

Separating Business Rules from Data Access

01 March 2015

Separating Business Rules from Data Access thumbnail

Business rules can be mixed into data access code. This scenario is a violation of the Single Responsibility Principle, because the data access class would have to change if the business rules change. In this post I will show you how to separate the business rules from the data access code.

Example

An example of data access code using Entity Framework that contains business rules is this:

using (var context = new MyContext())
{
    var eligibleUsers = context.Users
                               .Where(u => u.EmailVerified);
}

The content of the Where clause is a business rule that defines the condition for eligibility. If the condition changes, the data access class would also change. For example, if the condition for eligibility included having an age value, the code would become::

using (var context = new MyContext())
{
    var eligibleUsers = context.Users
                               .Where(u => u.EmailVerified &&
                                           u.Age != null);
}

The Where clause was changed to reflect the new business rule. Since this code is in the data access class, the data access class will change because of the change of the business rules. What we want is to extract the business rules to a different class.

Expressions to the Rescue

The content of the Where clause takes a parameter of type Expression<Func<T,bool>>. Entity Framework uses the Expression to create an appropriate SQL query. In the following example, an Expression is assigned to a variable and then later used in the Where clause:

Expression<Func<User, bool>> myExpression = u => u.EmailVerified &&
                                                 u.Age != null;

...

using (var context = new MyContext())
{
    var eligibleUsers = context.Users.Where(myExpression);
}

Even though the Expression type looks fancy, it’s the same as any other type and there’s no reason why it couldn’t be put in another class:

public static class UserRules
{
    public static Expression<Func<User, bool>> GetEligibilityCriteria()
    {
        return u => u.EmailVerified && u.Age != null;
    }
}

Which can then be used in the data access code like so:

using (var context = new MyContext())
{
    var eligibleUsers = context.Users
                               .Where(UserRules.GetEligibilityCriteria());
}

So now, when the eligibility criteria changes, only the UserRules class would change.

Conclusion

In this post, I showed you how to extract the contents of the Where clause in Entity Framework to a different class. This puts a separation between the data access and the business rules classes, enforcing the Single Responsibility Principle. This approach also promotes reusability (DRY) because the business rules could now also be used in other parts of the application (ex: in unit tests).