You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Clean Architecture第3、4层依赖规则的适配困惑求解

Clean Architecture第3、4层的依赖规则适配解析

首先明确Clean Architecture的层级划分(从内到外):

  • 第1层:实体(Entities)
  • 第2层:用例(Use Cases)
  • 第3层:接口适配器(Interface Adapters)—— 控制器、数据仓库抽象都属于这一层
  • 第4层:框架与驱动(Frameworks & Drivers)—— 数据库、UI框架、第三方库都属于这一层

你困惑的核心是如何让第3层不直接依赖第4层,关键在于运用依赖倒置原则(DIP),具体做法如下:

1. 在第3层定义抽象接口

第3层的控制器不会直接调用第4层的数据库实现,而是在第3层内部定义一个抽象的数据访问接口。比如:

// 第3层:接口适配器层的抽象
public interface UserRepository {
    User getUserById(Long id);
    void saveUser(User user);
}

控制器只依赖这个抽象接口,完全不知道第4层的具体数据库实现细节。

2. 在第4层实现抽象接口

所有和数据库相关的SQL操作,都放在第4层的实现类里,这个实现类依赖第3层的抽象接口:

// 第4层:框架与驱动层的实现
public class MySQLUserRepository implements UserRepository {
    @Override
    public User getUserById(Long id) {
        // 这里写具体的SQL查询逻辑
        String sql = "SELECT * FROM users WHERE id = ?";
        // 执行SQL并返回User对象
    }

    @Override
    public void saveUser(User user) {
        // 写SQL插入/更新逻辑
    }
}

此时源代码的依赖方向是第4层 → 第3层,完全符合「依赖只能向内指向」的规则——内圈(第3层)的抽象不了解外圈(第4层)的实现,反而外圈依赖内圈的定义。

3. 运行时通过依赖注入完成调用

编译期控制器只依赖第3层的抽象,运行时通过依赖注入(DI)框架把第4层的具体实现注入到控制器中。比如:

// 第3层:控制器
public class UserController {
    private final UserRepository userRepository;

    // 通过构造注入传入具体实现
    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUser(Long id) {
        return userRepository.getUserById(id);
    }
}

运行时调用是从控制器(第3层)到数据库实现(第4层),但这是运行时的调用方向,而Clean Architecture的依赖规则约束的是编译期的源代码依赖方向——后者才是决定架构耦合性的关键。

总结

  • 第3层(接口适配器)负责定义抽象,不涉及任何具体外部实现细节
  • 第4层(框架与驱动)负责实现这些抽象,所有SQL、第三方API调用都限制在这里
  • 依赖方向始终是外圈依赖内圈,完全符合Uncle Bob的依赖规则

内容的提问来源于stack exchange,提问作者JPFrancoia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 03:20:13