首页/开发/系统分析与架构设计速查/规约模式(Specification)
架构风格与模式58 条

规约模式(Specification)

把业务规则封装为可命名、可组合(与/或/非)的规约对象,既可用于校验,也可用于构造查询条件,让规则复用且保持业务语义。

什么是规约模式

规约模式(Specification)把业务规则封装成可命名、可组合(与、或、非)的对象。它既能用于内存校验(给定对象判断是否满足规则),也能翻译成查询条件(拼装 WHERE 子句),从而让规则在多处复用时保持业务语义。埃里克·埃文斯在《领域驱动设计》中把它列为通用模式之一。

基本信息

核心能力命名规则、逻辑组合(与或非)、内存校验与查询翻译双用
适用条件规则需要多处复用、需要组合、或需要在查询中表达同一语义
主要风险内存判断与数据库查询两套实现不一致,导致结果差异
落地约束关注可读性与索引友好性,避免规则嵌套过深

典型使用场景

  • 订单筛选:把「已支付且金额超过阈值且未退款」封装成可复用的规约
  • 促销资格判断:规则同时用于下单校验与活动列表筛选
  • 权限与合规:把准入条件组合成命名规则,便于审计与说明
  • 报表与后台查询:把业务规则翻译为查询条件,避免两处口径不一
  • 单元测试:用同一规约做快速内存校验,减少对数据库的依赖
  • 规则演进:新增条件只需组合新规约,不改动调用方

组合与双路径使用

组合与双路径使用
# 组合示意
spec = 已支付.and(金额超过(1000)).and(非(已退款))
if spec.is_satisfied_by(order):
    允许发货

# 双路径:内存校验与数据库查询共用同一规约定义
spec = 已支付.and(金额超过(1000))
# ORM 侧:翻译为 WHERE 条件(注意字段是否有索引)
#   WHERE status = 'paid' AND amount > 1000
# 内存侧:同一规约用于单元测试与快速校验
# 一致性保障:用对照测试锁定两条路径的输出差异

两种使用路径对照

路径使用方式注意点
内存校验对已加载对象判断是否满足对象字段需完整加载,避免误判
查询翻译翻译为查询条件交给数据库索引友好性决定性能,避免函数包裹字段
组合复用把多个规约组合成新规约层数控制,超过三层需拆分命名
测试锁定两种路径结果一致的对照测试覆盖边界值与空值场景
规约的收益在命名:给规则起一个业务名字(如「可发货」)比在代码里散落条件判断更容易理解和评审。
内存校验与查询翻译是两套实现:若不做对照测试,字段语义或空值处理差异会让两处结果悄悄分叉。

易错点

  • 规约嵌套过深,规则无法阅读与调试,排查时难以定位哪个条件命中
  • 内存校验与数据库查询两套实现不一致,同一业务在两处结果不同
  • 把复杂规则翻译成难以索引的查询条件,性能随数据量迅速下降
  • 把规约当成通用查询构造器,规则失去业务语义,退化成语法糖

数据来源与参考

  • Eric Evans《Domain-Driven Design》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌