一、什么是规约(Specification)?
规约(Specification)是领域驱动设计中的一种重要模式,属于战术设计层。它用来表达对象应该满足的业务规则或条件(即“什么是合格的”)。规约可以复用、组合,使复杂的业务规则易于表达、维护与测试。
简单来说:规约负责以可表达、可重用的对象形式表示企业的业务规则和标准。
二、规约的典型应用场景
三、DDD中规约的结构
通常,Specification模式包括:
- 接口/抽象类:定义isSatisfiedBy方法,判断某对象是否满足规约。
- 具体实现:针对某些具体业务条件实现isSatisfiedBy。
- 规约组合:可以通过And、Or、Not等方式组合规约。
典型接口定义如下(伪代码,更偏Java/C#风格):
public interface Specification<T> {
boolean isSatisfiedBy(T candidate);
Specification<T> and(Specification<T> other);
Specification<T> or(Specification<T> other);
Specification<T> not();
}
四、规约实现举例
以“用户”聚合为例,用Java仿写演示:
public interface Specification<T> {
boolean isSatisfiedBy(T candidate);
default Specification<T> and(Specification<T> other) {
return candidate -> this.isSatisfiedBy(candidate) && other.isSatisfiedBy(candidate);
}
default Specification<T> or(Specification<T> other) {
return candidate -> this.isSatisfiedBy(candidate) || other.isSatisfiedBy(candidate);
}
default Specification<T> not() {
return candidate -> !this.isSatisfiedBy(candidate);
}
}
public class EnabledUserSpecification implements Specification<User> {
@Override
public boolean isSatisfiedBy(User user) {
return user.isEnabled();
}
}
public class RichUserSpecification implements Specification<User> {
@Override
public boolean isSatisfiedBy(User user) {
return user.getBalance() > 1000;
}
}
// 组合使用
Specification<User> enabledAndRich = new EnabledUserSpecification().and(new RichUserSpecification());
User u = …;
if(enabledAndRich.isSatisfiedBy(u)){
// 满足规约
}
五、规约在实际开发中的作用
六、一些实用建议
- 没有复杂规则,不必引入规约,保持简洁。
- 可将规约作为领域服务的一部分,实现时关注业务含义;
- 对于需要持久化的规约(如数据库查询),可用如JPA Specification等工具。
七、参考资料
- 《领域驱动设计:软件核心复杂性应对之道》(Eric Evans)
- 《实现领域驱动设计》(Vaughn Vernon)
- Specification Pattern (Martin Fowler)
八、规约与领域模型、领域服务的关系
在DDD中,业务规则可能分散在实体、值对象、聚合根和领域服务中。规约的职责与它们有交集也有区别:
- 实体自身的简单规则(如有某个属性、非空校验),可以直接放在实体方法里。
- 涉及多个对象或者复杂业务规则,适合用规约表达。
- 规约本身可以是领域服务的一部分(如某些跨聚合的校验)。
- 在聚合根保证一致性边界时,可以通过规约辅助判断修改是否合法。
九、业务用例示例(更贴近真实开发)
假设我们有一个电商系统,订单需要满足“可取消”这一业务逻辑:
1. 可取消规约(OrderCanBeCanceledSpecification)
领域专家对规则的描述
- 未支付的订单可随时取消
- 已支付、未发货订单可取消
- 一旦发货则不可取消
- 已完成或已关闭订单不可取消
代码实现(以Java、C#为例)
public class OrderCanBeCanceledSpecification implements Specification<Order> {
@Override
public boolean isSatisfiedBy(Order order) {
if (order.getStatus() == OrderStatus.CLOSED || order.getStatus() == OrderStatus.COMPLETED) {
return false;
}
if (order.getStatus() == OrderStatus.SHIPPED) {
return false;
}
return true; // PENDING(未支付)或 PAID(已支付未发货)可以取消
}
}
应用方式
Order order = …;
Specification<Order> canCancel = new OrderCanBeCanceledSpecification();
if (canCancel.isSatisfiedBy(order)) {
order.cancel();
} else {
throw new BusinessException("订单不可取消!");
}
十、规约的组合与复用
假设你在系统中有越来越多的业务规则。引入规约后,你可以通过组合,实现“与”“或”“非”等复合逻辑。
组合示例
Specification<Order> paid = order -> order.isPaid();
Specification<Order> notShipped = order -> !order.isShipped();
Specification<Order> canRefund = paid.and(notShipped);
if (canRefund.isSatisfiedBy(order)) {
order.refund();
}
这样,假如你的规则变了,“已支付且未完成的订单可退款”,只需组合不同的规约。
十一、规约的持久化扩展(数据库查询规约)
在领域层判断规约往往是针对内存对象,但有场景需要用规约构造数据库查询(如JPA Specification、EF Core Specification等)。
以JPA为例
public class EnabledUserSpecification implements Specification<User> {
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("enabled"), true);
}
}
你可以将领域规约转化为数据库查询规约,实现业务规则与持久层规则的解耦与统一。
十二、其他语言示例(Python)
class Specification:
def is_satisfied_by(self, candidate):
raise NotImplementedError
def __and__(self, other):
return AndSpecification(self, other)
class AndSpecification(Specification):
def __init__(self, spec1, spec2):
self.spec1 = spec1
self.spec2 = spec2
def is_satisfied_by(self, candidate):
return self.spec1.is_satisfied_by(candidate) and self.spec2.is_satisfied_by(candidate)
class EnabledUserSpecification(Specification):
def is_satisfied_by(self, user):
return user.enabled
class RichUserSpecification(Specification):
def is_satisfied_by(self, user):
return user.balance > 1000
# 使用
spec = EnabledUserSpecification() & RichUserSpecification()
if spec.is_satisfied_by(user):
# 业务逻辑
十三、规约在验证、权限、限流等的应用延展
- 验证:将校验逻辑(如邮箱唯一、手机号校验等)封装为规约,可以复用于多业务流程。
- 权限/授权:可将访问权限判断封装为规约,易于组合各种授权策略。
- 限流/风控:对于风控规则快速变化场景,使用规约可弹性组合和测试。
十四、实践建议
总结
规约 是DDD重要的战术模式之一,能抽象、表达并组合领域对象的满足条件,为业务规则实现提供良好支持。实际应用中,要根据项目复杂度酌情采用,利用好规约的复用与组合价值。





