业务技能开发实战:从零构建高可维护性业务逻辑层

2次阅读
没有评论

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

image.webp

混乱业务逻辑的代价

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

业务技能开发实战:从零构建高可维护性业务逻辑层

这个典型场景暴露了业务逻辑开发的三大痛点:

  1. 逻辑与实现强耦合:折扣规则变更需要修改核心计算方法
  2. 缺乏明确边界:库存校验等跨领域逻辑混在其中
  3. 难以测试验证:无法对单个优惠规则进行独立测试

架构选型:事务脚本 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) 时,不妨停下来想想:这个逻辑半年后还会保持现在的样子吗?

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