Java整洁架构案例

wen java案例 1

目录导读

  1. 为什么你的Java项目三个月后变成了“大泥球”?
  2. 整洁架构的核心思想:依赖规则与边界
  3. 案例实战:一个订单系统的整洁架构重塑
    • 1 领域层(Entities):业务规则的“家”
    • 2 用例层(Use Cases):编排业务动作
    • 3 适配器层(Adapters):与Spring/数据库解耦
    • 4 框架层(Frameworks):UI与外部工具
  4. 关键问答:整洁架构的四个灵魂拷问
  5. 落地建议:从遗留代码迁往整洁架构的路径

为什么你的Java项目三个月后变成了“大泥球”?

很多Java团队初期规划得很好,但几个月后代码库就变成“意大利面条”:Controller直接操作Repository,Service类上千行,业务逻辑散落在DTO和工具类中,根本原因在于没有强制依赖边界

Java整洁架构案例

整洁架构(Clean Architecture,由Robert C. Martin提出)的价值在于:让业务规则独立于框架、UI、数据库和外部服务,它不是银弹,但能在Java项目里显著降低认知负担和回归故障率。

整洁架构的核心思想:依赖规则与边界

整洁架构用同心圆模型表示依赖方向:外层依赖内层,内层绝不依赖外层,依赖规则只有一条:源代码依赖必须指向内层

  • 实体(Entities):最内层,包含企业级业务规则(如订单状态机)。
  • 用例(Use Cases):第二层,编排实体完成特定用户场景(如“提交订单”)。
  • 接口适配器(Interface Adapters):第三层,将数据转换为用例/实体需要的格式(如JPA Entity转换为领域Entity)。
  • 框架/驱动(Frameworks & Drivers):最外层,如Spring MVC Controller、MySQL驱动、消息队列客户端。

关键点:业务逻辑不感知Spring注解、不依赖@Transactional、不关心是Controller还是RMI调用。

案例实战:一个订单系统的整洁架构重塑

假设我们有一个传统Spring Boot订单服务,最初代码结构是:Controller -> Service -> Repository,我们将重构为四层。

1 领域层(Entities)—— 订单的核心

// 无Spring依赖,纯Java
public class Order {
    private OrderId id;
    private List<OrderItem> items;
    private OrderStatus status;
    public void addItem(Product product, int quantity) {
        // 业务规则:商品不可重复?库存校验由用例层处理,这里只做状态验证
        if (status != OrderStatus.DRAFT) throw new IllegalStateException("只能修改草稿订单");
        items.add(OrderItem.create(product, quantity));
    }
    public Money calculateTotal() {
        return items.stream().map(OrderItem::subtotal).reduce(Money.ZERO, Money::add);
    }
}

这里没有getter/setter暴徒,而是行为集中的领域模型。

2 用例层(Use Cases)—— 编排业务动作

用例类对应一个用户故事,例如SubmitOrderUseCase

public class SubmitOrderUseCase {
    private final OrderRepository orderRepository; // 接口,定义在用例层
    private final InventoryGateway inventoryGateway; // 外部接口,定义在用例层
    public SubmitOrderUseCase(OrderRepository repo, InventoryGateway inv) {
        this.orderRepository = repo;
        this.inventoryGateway = inv;
    }
    public SubmitOrderResult execute(SubmitOrderCommand command) {
        Order order = orderRepository.findById(command.orderId());
        order.submit(); // 状态机转换
        boolean reserved = inventoryGateway.reserve(order.getLineItems());
        if (!reserved) throw new BusinessRuleException("库存不足");
        orderRepository.save(order);
        return new SubmitOrderResult(order.getId());
    }
}

注意OrderRepository是接口,真正的数据库实现放在外层。

3 适配器层(Adapters)—— 与Spring/数据库解耦

  • 持久化适配器JpaOrderRepository implements OrderRepository,内部使用Spring Data JPA。
  • 网关适配器HttpInventoryGateway implements InventoryGateway,调用外部库存微服务。
  • 输入适配器:Spring Controller OrderController 接受HTTP请求,校验参数,构造SubmitOrderCommand,调用用例。

4 框架层(Frameworks)—— 配置与启动

Spring配置文件、数据库连接、消息监听器都在这层,这层“脏”是允许的,但它必须不污染内层。

依赖方向JpaOrderRepository依赖OrderRepository接口(内层),但内层完全不依赖JPA,这通过依赖倒置实现。

关键问答:整洁架构的四个灵魂拷问

Q1: 这是不是过度设计?小项目也值得用吗? A: 如果你做的是CRUD页面、没有复杂业务规则,确实可能过度。判断标准:未来会有多个UI(Web/控制台/API)、业务规则会变化(如折扣算法)、需要替换数据库或框架,有其一,就值得采用,对于纯CRUD,建议模块化单层即可。

Q2: 如何在Spring中管理用例的生命周期? A: 将用例类注册为Spring的@Service,但用例内部不依赖Spring,Spring的扫描器默认会注入构造函数依赖,但我们的依赖是接口——所以在配置类中定义@Bean手动绑定实现,如果用例需要事务,可以在适配器层加@Transactional,但更干净的做法是使用装饰器模式包裹用例。

Q3: 如何处理跨用例的事务? A: 整洁架构建议:用例编排事务,即在用例执行入口(例如Controller调用用例的方法)上添加事务边界,这会使内层依赖了事务的概念,更优解:使用“命令总线”或UnitOfWork模式,在适配器层捕获异常并回滚,实际项目中,大多数团队妥协:在用例层方法上使用@Transactional(仅当项目明确绑定了Spring,或者你愿意在用例层保留一个极薄的接口注解)。

Q4: 领域模型贫血导致内层变成“数据袋子”,如何避免? A: 那是贫血模型(Anemic Domain)的问题,不是整洁架构的错,解决方案:将行为尽量放入实体,例如订单状态机、金额计算,如果实体实在没有复杂行为,那说明你构建的是“事务脚本”,适合直接用用例层+DTO,不必强行分层。

落地建议:从遗留代码迁往整洁架构的路径

不要“推倒重来”,分三步走:

  1. 识别边界:以领域名词(订单、用户、库存)划出模块,找出目前Service里的“非业务代码”(如HTTP调用、SQL拼接),移到适配器。
  2. 依赖反转:为现有Repository引入接口,让Service依赖接口,再创建Jpa实现类,让Controller只调用UseCase,而不是直接调用Service方法。
  3. 渐进式移动逻辑:每周搬移一个用例,优先处理变化频繁的业务,用ArchUnit或依赖分析工具(如JDepend)写测试防止新的反向依赖。

整洁架构不是代码模板,而是一种纪律,它要求团队在每次写代码前问自己:“这行代码该住哪一层?”坚持三个月,你的Java项目会从“大泥球”变成“洋葱”,虽然剥开会流泪,但每一层都清晰可循。

【广告位保持空白,尊重用户体验】

抱歉,评论功能暂时关闭!