What is specification pattern?
Specification pattern is a software designing pattern where business logic is maintained in single unit which can then be used to either validate business logic against domain model or can be used to chain business rules together using Boolean logic operands like AND,OR,NOT to retrieve expected results.
In this article, we will discuss about specification pattern. We will cover brief overview of specification pattern, its use in domain-driven development and its usefulness to keep DRY (Don’t Repeat Yourself) principle intact while implementing business logic. We will also discuss implementation of specification pattern together with repository pattern and c#’s generics to obtain generic abstract specification which will provide base of pattern.
Example
Let’s consider famous Employee as an entity with following property.
public class Employee { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public string Name { get; set; } public Designation Position { get; set; } public int TeamLeaderId { get; set; } } public enum Designation { CEO, Manager, TeamLeader, SeniorDeveloper, JuniorDeveloper }
Now suppose there is need to get the detail of Employees which are team leaders, here we will create EmployeeRepository which interacts with Employee data source.
So, we will create EmployeeRepository.cs along with IEmployeeRepository.cs
public class EmployeeRepository:IEmployeeRepository { private readonly IDataRepository<Employee> _employeeRepository; public EmployeeRepository(IDataRepository<Employee> employeeRepository) { _employeeRepository = employeeRepository; } //method to get the list of team public List<Employee> GetTeamLeaders() { return _employeeRepository.Fetch(x=>x.Position==Designation.TeamLeader).ToList(); } }
OK, this looks simple. It could be complicated as more and more requirement increases.it creates problem when there is need to validate the logic as well as need to get list of employees from the repository method.
For example if at some point of time there is a need to check that Employee’s Designation is Team leader. So controller method can be written as
public IHttpActionResult GetTeamMembers(int empId) { var emp = _employeeRepository.GetEmployeeById(empId); if (emp.Position != Designation.TeamLeader) { return BadRequest("Employee is not a team leader"); } return Ok(); }
In above code there is problem, as logic of deciding team leader is spread across two parts of application, one in GetTeamMembers method in controller and other in GetTeamLeaders method of repository. Which violates DRY principle and increases overhead to maintain code.
It becomes severe as size of code increases. So, to solve this problem specification pattern helps in keeping business logic in one single piece of unit.
So let’s add specification for Employee.
We will create specification for employee to decide team leader,
public class TeamLeaderSpecification { public Expression<Func<Employee, bool>> ToExpression() { return (x => x.Position == Designation.TeamLeader); } public bool IsSatisfiedBy(Employee employee) { var predicate=ToExpression().Compile(); return predicate(employee); } }
In above we have created class which contains specification to get expression which gives list of team leader and IsSatisfiedBy method provide way to validate the logic in controller.
Now we can use this specification to validate team leader criteria as follows,
var emp = _employeeRepository.GetEmployeeById(empId); var teamLeaderSpec = new TeamLeaderSpecification(); if (!teamLeaderSpec.IsSatisfiedBy(emp)) { return BadRequest("Employee is not team leader"); }
And repository method can be refactored as,
//Add method to get the list of team public List<Employee> GetTeamLeaders() { var teamLeaderSpecification = new TeamLeaderSpecification(); return _employeeRepository.Fetch(teamLeaderSpecification.ToExpression()).ToList(); }
Ok, so we have removed redundancy to decide team leader. But what if we also need to create specification for Company entity?
This is where Generics helps to provide specification for other entity.
Let’s create generic specification class.
public abstract class Specification<T> { public abstract Expression<Func<T, bool>> ToExpression(); public bool IsSatisfiedBy(T entity) { Func<T, bool> predicate = ToExpression().Compile(); return predicate(entity); } }
we have created this class as abstract, it make sense as it has method defined without implementation, that method will be overridden by the other specifications and they will decide how to implement ToExpression method. So let’s inherit this class to TeamLeaderSpecification class first.
Now TeamLeaderSpecification will looks like below,
public class TeamLeaderSpecification:Specification<Employee> { public override Expression<Func<Employee, bool>> ToExpression() { return (x => x.Position == Designation.TeamLeader); } }
Notice that ToExpression is overridden here and IsSatisfiedBy method is removed from TeamLeaderSpecification, actually it is moved to Specification class.
Again we are ready to create CompanySpecification as per company requirements and move on to expand Specification pattern. Like if we want to get the specification to get the company based on Id we would create,
public class CompanySpecificationById : Specification<Company> { private readonly int _id; public CompanySpecificationById(int id) { _id = id; } public override Expression<Func<Company, bool>> ToExpression() { return (x=>x.Id==_id); } }
Here we have created specification using constructor call as id would be dynamic, so whenever we need to create specification we will created specification using constructor initialization.
So we are ready to use specification for different entities.
However we can also chain the specifications Like AND, OR, NOT
For example if we want to combine two specifications like we want to have employees with name Mark and Also he should be team leader then we can create AND specification in abstract class,
public abstract class Specification<T> { public abstract Expression<Func<T, bool>> ToExpression(); public bool IsSatisfiedBy(T entity) { Func<T, bool> predicate = ToExpression().Compile(); return predicate(entity); } public Specification<T> And(Specification<T> specification) { return new AndSpecification<T>(this, specification); } } public class AndSpecification<T> : Specification<T> { private readonly Specification<T> _left; private readonly Specification<T> _right; public AndSpecification(Specification<T> left, Specification<T> right) { _right = right; _left = left; } public override Expression<Func<T, bool>> ToExpression() { Expression<Func<T, bool>> leftExpression = _left.ToExpression(); Expression<Func<T, bool>> rightExpression = _right.ToExpression(); BinaryExpression andExpression = Expression.AndAlso(leftExpression.Body, rightExpression.Body); return Expression.Lambda<Func<T, bool>>(andExpression, leftExpression.Parameters.Single()); } }
We can now create Specification to generate query to get team leaders by name as follows,
public List<Employee> GetTeamLeaderByName(string name) { var nameSpecification = new EmployeeNameSpecification(name); var teamLeaderSpecification = new TeamLeaderSpecification(); return _employeeRepository.Fetch(nameSpecification.And(teamLeaderSpecification).ToExpression()).ToList(); }
We have now two methods to get the list of employees,
public List<Employee> GetTeamLeaderByName(string name) { var nameSpecification = new EmployeeNameSpecification(name); var teamLeaderSpecification = new TeamLeaderSpecification(); return _employeeRepository.Fetch(nameSpecification.And(teamLeaderSpecification).ToExpression()).ToList(); } //method to get the list of team public List<Employee> GetTeamLeaders() { var teamLeaderSpecification = new TeamLeaderSpecification(); return _employeeRepository.Fetch(teamLeaderSpecification.ToExpression()).ToList(); }
In both of the above methods we are creating Specifications in Repository, we can combine two methods in one to get the list of employees and give the responsibility of creating business rules to controller and give single responsibility to get employee list to Repository. We can do it by simply creating method in employee repository which takes specification of employees and give list of employees back,
public List<Employee> GetEmployees(Specification<Employee> employeeSpecification) { return _employeeRepository.Fetch(employeeSpecification.ToExpression()).ToList(); }
that’s it, we can now call this method from controller and force the controller to create business rule.
public IHttpActionResult GetTeamLeaders() { var teamLeaderSpecification = new TeamLeaderSpecification(); var teamLeaders = _employeeRepository.GetEmployees(teamLeaderSpecification); return Ok(teamLeaders); }
Conclusion
- Specification pattern helps to implement DRY principle along with repository pattern and Generics.
- It also helps in implementing SRP (Single Responsibility Principle) and writing loosely coupled code.
- Specification pattern also helps to maintain entity related business logic in single unit of code.