I think Specification pattern is not designed for query criteria. Actually, the whole concept of DDD is not, either. Consider CQRS if there are plethora of query requirements.
Specification pattern helps develop ubiquitous language, I think it’s like kind of a DSL. It declares what to do rather than how to do it. For example, in a ordering context, orders are considered as overdue if it was placed but not paid within 30 minutes. With Specification pattern, your team can talk with a short but unique term: OverdueOrderSpecification. Imagine the discussion below:
case -1
Business people: I want to find out all overdue orders and ...
Developer: I can do that, it is easy to find all satisfying orders with an overdue order specification and..
case -2
Business people: I want to find out all orders which were placed before 30 minutes and still unpaid...
Developer: I can do that, it is easy to filter order from tbl_order where placed_at is less that 30minutes before sysdate....
Which one do you prefer?
Usually, we need a DSL handler to parse the dsl, in this case, it may be in the persistence adapter, translates the specification to a query criteria. This dependence (infrastrructure.persistence => domain) does not violates the architecture principal.
class OrderMonitorApplication {
public void alarm() {
// The specification pattern keeps the overdue order ubiquitous language in domain
List<Order> overdueOrders = orderRepository.findBy(new OverdueSpecification());
for (Order order: overdueOrders) {
//notify admin
}
}
}
class HibernateOrderRepository implements orderRepository {
public List<Order> findBy(OrderSpecification spec) {
criteria.le("whenPlaced", spec.placedBefore())//returns sysdate - 30
criteria.eq("status", spec.status());//returns WAIT_PAYMENT
return ...
}
}