In this post I want to talk a bit about Specification, a Domain-Driven Design pattern I think very interesting and useful in a lot of situations.
What is, so, a Specification?
Domain models are not only about entities, relations and collaborations. Domain models must also host a lot of implicit concepts and rules you must deal with, because they are important parts of your business domain : for example, booking policies, payment delinquent rules and so on.
The problem is to find a place for this concepts.
You can model and implement them inside your entities, your business objects, but doing so you couple your objects with concepts regarding them but not being part of them; more important :
- You lose a lot of expressivity, because you sink your meaningful (but implicit) concepts into your object code.
- You lose a lot of flexibility, because you cannot change or apply them regardless of your business objects.
Specifications, as said by Eric Evans, are a concept borrowed from logic programming: they are tiny classes with a method representing a boolean predicate.
The Specification models a particular concept that applies to a particular entity, providing a method which expresses the concept as a predicate; in particular, the method tests if a given instance of the entity satisfies the expressed predicate.
Let me show you a practical example.
Say you have an employee who issues some requests, for example permit requests.
The employee cannot directly request a permit for a number of days greater than those remaining; if he wants to request more days, he must ask the secretary, but even in this case he cannot exceed a certain limit.
How do you model this?
You'll have an Employee entity, a generic Request and a PermitRequest entity.
But what about all those concepts regarding the employee request?
Remember, a permit request which exceed the employee remaining days is not valid if made by the employee, and so on ...
If you don't use Specifications, you can implement a validation method inside your PermitRequest class :
public class PermitRequestDoing so, however, you couple your rules and cannot separately apply them, breaking your requirements!
implements Request, ValidationAware {
private static final int limit = -10;
private Employee owner;
private int requestedDays;
// ...
public void validate() {
int remaining = owner.getRemainingDays();
if (remaining < this.requestedDays) {
// notice this ...
}
if ((remaining - this.requestedDays)
< PermitRequest.limit) {
// notice this ...
}
// ...
}
}
You can surely use two distinct validation methods:
public class PermitRequestBut doing so you couple yourself with the particular Request entity, breaking a lot of design principles ... what if we want to validate a request without knowing its actual type? What method to call?
implements Request {
private static final int limit = -10;
private Employee owner;
private int requestedDays;
// ...
public void validateForEmployee() {
int remaining = owner.getRemainingDays();
if (remaining < this.requestedDays) {
// notice this ...
}
// ...
}
public void validateForSecretary() {
int remaining = owner.getRemainingDays();
if ((remaining - this.requestedDays)
< PermitRequest.limit) {
// notice this ...
}
// ...
}
}
Implementing two distinct Specifications is simple and solves all your problems.
You'll have an ExceedDaysSpecification for the first rule (requested day cannot exceed remaining days), and an ExceedLimitSpecification for the second one (requested days cannot exceed a certain limit), each with the predicate method accepting a Request and testing the given rule over it.
public class ExceedDaysSpecification
implements RequestSpecification {
public boolean isValid(Request request) {
int remaining = request.getOwner().getRemainingDays();
int requested = request.getRequestedDays();
if (remaining < requested) {
return false;
}
else {
return true;
}
}
}
In this way :
public class ExceedLimitSpecification
implements RequestSpecification {
private static final int limit = -10;
public boolean isValid(Request request) {
int remaining = request.getOwner().getRemainingDays();
int requested = request.getRequestedDays();
if ((remaining - requested)
< ExceedLimitSpecification.limit) {
return false;
}
else {
return true;
}
}
}
- Implicit concepts are powerfully expressed.
- Specifications can be applied to every base type of entity (Request) : if you need to know the actual type inside the Specification predicate method, you can do a downcast or apply a simplified Visitor pattern.
- Specifications can be easily combined : simply combine the evaluation of the predicate methods!
What's your way of thinking about this?