共计 2640 个字符,预计需要花费 7 分钟才能阅读完成。
混乱业务逻辑的代价
最近接手了一个订单优惠计算模块,当我看到超过 800 行的 calculateDiscount() 方法时,倒吸一口凉气。方法里嵌套了 12 层 if-else,还夹杂着直接调用库存服务的远程调用。更可怕的是,上周营销部门新增的 ” 老客阶梯折扣 ” 规则,导致原有逻辑出现了难以排查的金额计算错误。

这个典型场景暴露了业务逻辑开发的三大痛点:
- 逻辑与实现强耦合:折扣规则变更需要修改核心计算方法
- 缺乏明确边界:库存校验等跨领域逻辑混在其中
- 难以测试验证:无法对单个优惠规则进行独立测试
架构选型:事务脚本 vs 领域模型
事务脚本模式
-
优点:快速实现,适合简单业务场景
// 典型的事务脚本示例 public void placeOrder(OrderDTO dto) { // 验证库存 boolean available = inventoryService.check(dto.getItems()); // 计算金额 BigDecimal amount = calculateAmount(dto); // 扣减库存 inventoryService.reduce(dto.getItems()); // 创建订单 orderDao.create(dto); } -
缺点:随着业务复杂化会变成 ” 面条代码 ”
领域模型模式
- 优点:业务内聚,更符合复杂业务场景
// 领域对象示例 public class Order { private List<OrderItem> items; private DiscountStrategy discountStrategy; public void applyDiscount() {this.discountStrategy.apply(this); } }
选型建议:
– 业务规则不超过 10 个且无交叉影响 → 事务脚本
– 需要处理多变的业务规则 → 领域模型
– 混合方案:核心领域用模型,周边操作用脚本
分层架构实战
1. 策略模式处理业务规则
定义折扣策略接口:
public interface DiscountStrategy {void apply(Order order);
boolean support(OrderContext context);
}
实现具体策略(示例为新客首单折扣):
public class NewUserDiscount implements DiscountStrategy {
@Override
public void apply(Order order) {if (order.getUser().isNew()) {order.setAmount(order.getAmount().multiply(0.9));
}
}
@Override
public boolean support(OrderContext ctx) {return ctx.contains("new_user");
}
}
2. 工厂方法隔离对象创建
public class DiscountFactory {
private List<DiscountStrategy> strategies;
public DiscountStrategy getStrategy(OrderContext ctx) {return strategies.stream()
.filter(s -> s.support(ctx))
.findFirst()
.orElseThrow(()->new BusinessException("无适用折扣策略"));
}
}
3. DTO 与领域对象转换
使用 MapStruct 实现高效转换:
@Mapper
public interface OrderMapper {OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
@Mapping(target = "user", source = "userId")
Order toDomain(OrderDTO dto);
}
生产环境关键要点
事务边界控制
- 领域操作使用
@Transactional注解 - 跨服务调用考虑 Saga 模式
并发冲突处理
- 乐观锁实现示例:
@Version private Long version; public void updateOrder(Order order) {Order exist = orderDao.findById(order.getId()); if (order.getVersion() != exist.getVersion()) {throw new OptimisticLockException(); } // ... 更新操作 }
监控埋点策略
- 使用 Spring AOP 记录业务操作:
@Aspect @Component public class BizMonitor {@Around("@annotation(bizLog)") public Object log(ProceedingJoinPoint pjp, BizLog bizLog) {long start = System.currentTimeMillis(); try {return pjp.proceed(); } finally {Metrics.timer(bizLog.value()) .record(System.currentTimeMillis() - start, MILLISECONDS); } } }
进阶思考:热加载实现
考虑以下技术路线:
1. 规则配置化:将业务规则存储在数据库或配置中心
2. Groovy 脚本引擎:动态加载业务逻辑
3. Java Agent:类重新定义(需考虑兼容性)
// Groovy 热加载示例
public class RuleEngine {
private ScriptEngine engine;
public void refresh(String script) {this.engine = new GroovyScriptEngine().parse(script);
}
public Object execute(Map<String,Object> params) {return engine.run(params);
}
}
写在最后
重构那个折扣模块后,代码量从 800 行缩减到核心的 200 行,但更重要的是现在营销同事可以通过实现 DiscountStrategy 接口自主添加新规则。当看到他们第一次不依赖研发就上线了会员日的特殊折扣时,我知道这次架构升级真的值了。
下次当你发现业务逻辑里又出现那个熟悉的 if (type == A) {...} else if (type == B) 时,不妨停下来想想:这个逻辑半年后还会保持现在的样子吗?
