从规则引擎到生产实践:如何用 spec 和 command 构建高可维护的业务逻辑

4次阅读
没有评论

共计 2400 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

背景痛点:为什么我们需要规则引擎

在电商优惠券、金融风控等业务场景中,我们经常遇到这样的困境:

从规则引擎到生产实践:如何用 spec 和 command 构建高可维护的业务逻辑

  • 业务规则频繁变更(比如每周调整营销策略)
  • 多重条件组合导致 if-else 嵌套爆炸(风控规则常超过 10 层判断)
  • 业务人员希望自主调整规则权重

传统硬编码方式会产生这些问题:

  1. 每次修改都需要重新发布应用
  2. 历史规则难以追溯(Git 记录无法直观体现业务语义)
  3. 测试用例随着规则增长呈指数级增加

这正是 DDD 中规则模式(Rules Pattern)要解决的问题——将易变的业务规则从核心域逻辑中剥离。

技术选型:规则引擎横向对比

主流 Java 规则引擎能力矩阵:

引擎 学习成本 动态更新 性能 适用场景
Drools 支持 复杂规则链
EasyRules 不支持 简单规则集
MVEL 支持 脚本式规则

选择 Drools 的核心原因:

  • spec 驱动 :通过 DRL/YAML 声明式定义规则,业务人员可读
  • Rete 算法 :对多条件规则有优化,避免重复计算
  • AgendaGroup:支持规则分组执行,实现灰度发布

实现方案:Spring Boot 集成 Drools

基础环境搭建

  1. 添加 Maven 依赖(注意匹配 Spring Boot 版本):
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.59.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-decisiontables</artifactId>
    <version>7.59.0.Final</version>
</dependency>
  1. 创建规则定义文件(resources/rules/discount.drl):
rule "VIP 用户折扣"
    agenda-group "discount"
    when
        $user : User(level == "VIP", $total: totalAmount)
        $order : Order(amount >= 1000)
    then
        $order.setDiscount($total * 0.2);
end

核心服务实现

动态加载规则的服务类:

@RefreshScope
@Service
public class RuleService {
    private final KieContainer kieContainer;

    // 通过 Command 模式执行规则
    public void executeRules(Object fact, String agendaGroup) {KieSession session = kieContainer.newKieSession();
        try {session.getAgenda().getAgendaGroup(agendaGroup).setFocus();
            session.insert(fact);
            session.fireAllRules();} finally {session.dispose();
        }
    }
}

测试用例验证

@Test
void should_apply_vip_discount() {User user = new User("VIP");
    Order order = new Order(1500);

    ruleService.executeRules(new Object[]{user, order}, 
        "discount"
    );

    assertEquals(300, order.getDiscount());
}

生产环境最佳实践

规则版本管理

采用 Git+ 数据库混合方案:

  1. 规则文件保存在 Git 仓库(便于 diff)
  2. 每次发布生成 MD5 指纹存入 DB
  3. 通过 /admin/rules 接口查看当前生效版本

性能优化要点

  • KieBase 缓存 :避免重复编译 DRL

    KieServices ks = KieServices.Factory.get();
    KieFileSystem kfs = ks.newKieFileSystem();
    kfs.write("src/main/resources/rules/discount.drl", 
        ks.getResources().newInputStreamResource(drlContent));
    KieBuilder kieBuilder = ks.newKieBuilder(kfs).buildAll();
    return ks.newKieContainer(kieBuilder.getKieModule().getReleaseId());

  • Session 复用 :对无状态规则使用 StatelessKieSession

监控方案

通过事件监听器采集指标:

session.addEventListener(new DefaultRuleRuntimeEventListener() {
    @Override
    public void objectInserted(ObjectInsertedEvent event) {Metrics.counter("rules.fired", "rule", event.getRule().getName()).increment();}
});

进阶思考:规则引擎 + 状态机

在订单状态流转等场景中,可以:

  1. 用状态机定义主流程(如:待支付→已支付→发货中)
  2. 用规则引擎处理状态转换条件(如:
  3. 支付超时自动取消
  4. 黑名单用户拦截发货)
  5. 通过 AgendaGroup 实现阶段式规则触发

这种组合方案既能保证主流程清晰,又能灵活处理业务异常分支。

总结

通过 Drools 实现规则与代码解耦后,我们获得了:

  • 规则变更实时生效(配合 Nacos 配置中心)
  • 业务逻辑可视化(通过决策表)
  • 历史版本可追溯(Git+DB 双存储)

建议从简单场景开始实践,逐步将核心业务规则迁移到引擎中管理。当规则超过 20 条时,这种架构的优势会越来越明显。

正文完
 0
评论(没有评论)