Clean Architecture(整洁架构/干净架构)是由 Robert C. Martin(Bob 大叔) 在 2017 年提出的软件架构范式 ,其核心目标是通过分层设计严格的依赖规则,构建高可维护性、高可测试性且与技术细节解耦的系统。

核心思想

同心圆分层结构

分层结构、关注点分离

将系统划分为独立的模块,每个模块负责单一职责(如业务逻辑、数据访问、界面展示)。通常分为四层:实体(Entities)、用例(Use Cases)、接口适配器(Interface Adapters)、框架与驱动(Frameworks & Drivers)。

架构由四个同心圆组成,从中心向外依次是:

层级名称职责
最内层Entities(实体层)核心业务对象、企业级业务规则,独立于应用
第二层Use Cases(用例层或者应用层)应用特定的业务逻辑,协调实体完成具体业务流程,如“创建订单”用例。调用领域模型的方法,但不直接操作数据库或 UI。
第三层Interface Adapters(接口适配层)将数据转换为最内层或最外层所需的格式(如 MVC 中的 Presenters、Controllers、Gateways)
最外层Frameworks & Drivers(框架与驱动层)具体技术实现:UI 框架、数据库、Web 框架、外部设备等

依赖规则(The Dependency Rule)

代码依赖只能向内指向,外层依赖内层,内层绝不依赖外层。 核心业务逻辑(实体和用例)完全独立于UI、数据库、第三方服务等外部细节。

  • 内层(实体、用例)不知道外层的存在
  • 外层通过依赖倒置原则(DIP) 使用内层定义的接口
  • 跨层传递的数据结构必须简单(DTO、基本类型),避免违反依赖规则

独立性

业务逻辑应完全独立于外部依赖(如数据库、网络),便于单元测试。

业务规则不依赖于UI、数据库、框架等,使得系统可以轻松更换技术栈,也便于测试和演进。

Demo

下面通过一个简单的用户注册功能示例,展示 Clean Architecture 的分层结构和依赖倒置原则。示例使用 Java 编写,不依赖任何框架,以便清晰体现核心思想。

项目结构

src/
├── entity/                // 企业级业务实体
│   └── User.java
├── usecase/               // 应用业务用例
│   ├── RegisterUserInput.java
│   ├── RegisterUser.java
│   └── RegisterUserInteractor.java
├── repository/            // 仓储接口(核心层定义)
│   └── UserRepository.java
├── data/                  // 数据实现(外层,依赖接口)
│   └── InMemoryUserRepository.java
├── web/                   // 接口适配器(外层)
│   └── RegisterUserController.java
└── Main.java              // 组合启动类

代码实现

实体层(Entity)
package entity;
 
public class User {
    private String id;
    private String username;
    private String password; // 已加密的密码
 
    public User(String id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }
 
    // 业务规则:密码加密(简化版,实际应使用BCrypt等)
    public static String encryptPassword(String rawPassword) {
        return "encrypted_" + rawPassword; // 仅为演示
    }
 
    // getters...
    public String getId() { return id; }
    public String getUsername() { return username; }
    public String getPassword() { return password; }
}
仓储接口(核心层定义)
package repository;
 
import entity.User;
import java.util.Optional;
 
public interface UserRepository {
    void save(User user);
    Optional<User> findByUsername(String username);
}
用例层(Use Case)

输入模型(简单的数据传输对象)

package usecase;
 
public class RegisterUserInput {
    private String username;
    private String password;
 
    public RegisterUserInput(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    public String getUsername() { return username; }
    public String getPassword() { return password; }
}

用例接口(输入边界)

package usecase;
 
public interface RegisterUser {
    void execute(RegisterUserInput input) throws Exception;
}

用例实现(核心业务逻辑)

package usecase;
 
import entity.User;
import repository.UserRepository;
 
public class RegisterUserInteractor implements RegisterUser {
    private final UserRepository userRepository;
 
    // 依赖注入:只依赖接口,不依赖具体实现
    public RegisterUserInteractor(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    @Override
    public void execute(RegisterUserInput input) throws Exception {
        // 1. 检查用户名是否已存在
        if (userRepository.findByUsername(input.getUsername()).isPresent()) {
            throw new Exception("Username already exists");
        }
 
        // 2. 加密密码(业务规则)
        String encryptedPassword = User.encryptPassword(input.getPassword());
 
        // 3. 创建用户实体(通常ID由基础设施生成,这里简化用时间戳)
        User user = new User(String.valueOf(System.currentTimeMillis()), 
                             input.getUsername(), 
                             encryptedPassword);
 
        // 4. 保存
        userRepository.save(user);
    }
}
数据实现(外层)
package data;
 
import entity.User;
import repository.UserRepository;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
 
public class InMemoryUserRepository implements UserRepository {
    private final Map<String, User> store = new ConcurrentHashMap<>();
 
    @Override
    public void save(User user) {
        store.put(user.getUsername(), user);
    }
 
    @Override
    public Optional<User> findByUsername(String username) {
        return Optional.ofNullable(store.get(username));
    }
}
接口适配器(Controller)
package web;
 
import usecase.RegisterUser;
import usecase.RegisterUserInput;
 
public class RegisterUserController {
    private final RegisterUser registerUser;
 
    public RegisterUserController(RegisterUser registerUser) {
        this.registerUser = registerUser;
    }
 
    // 模拟HTTP POST /register 请求
    public String handleRequest(String username, String password) {
        try {
            RegisterUserInput input = new RegisterUserInput(username, password);
            registerUser.execute(input);
            return "User registered successfully!";
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        }
    }
}
组合启动(Main)
import data.InMemoryUserRepository;
import repository.UserRepository;
import usecase.RegisterUser;
import usecase.RegisterUserInteractor;
import web.RegisterUserController;
 
public class Main {
    public static void main(String[] args) {
        // 1. 创建外部依赖(数据实现)
        UserRepository userRepository = new InMemoryUserRepository();
 
        // 2. 创建用例(注入依赖)
        RegisterUser registerUser = new RegisterUserInteractor(userRepository);
 
        // 3. 创建控制器(注入用例)
        RegisterUserController controller = new RegisterUserController(registerUser);
 
        // 4. 模拟请求
        System.out.println(controller.handleRequest("alice", "secret123"));
        System.out.println(controller.handleRequest("alice", "another")); // 应报错
    }
}

运行输出:

User registered successfully!
Error: Username already exists

与 DDD 的关系

Clean Architecture 与领域驱动设计(DDD) 天然契合:

  • 领域层(Domain) 对应 Clean Architecture 的 Entities 层
  • 应用层(Application) 对应 Use Cases 层
  • 基础设施层(Infrastructure) 对应 Frameworks & Drivers 层

在严格分层架构中,每层只能依赖其直接下方的层,避免核心业务逻辑外泄 。

在 Android/Kotlin 中的实践

现代 Android 开发中,Clean Architecture 通常与 MVVM 结合使用:

┌─────────────────────────────────────┐
│  Presentation Layer (UI)            │  ← Activity/Fragment/Compose
│  - ViewModel                        │
│  - UI State                         │
├─────────────────────────────────────┤
│  Domain Layer (核心业务逻辑)          │  ← 纯 Kotlin,零 Android 依赖
│  - Entities (数据类)                 │
│  - Use Cases (业务用例)              │
│  - Repository Interfaces            │
├─────────────────────────────────────┤
│  Data Layer (数据实现)               │  ← Repository 实现、数据源
│  - RepositoryImpl                   │
│  - Remote Data Source (API)         │
│  - Local Data Source (DB)           │
└─────────────────────────────────────┘

关键实践要点

  1. Domain 层保持纯净:不依赖 Android SDK、协程、RxJava 等框架
  2. Use Case 原子化:每个用例只做一件事(如GetUserProfileUseCase
  3. 依赖注入:使用 Hilt/Koin 管理跨层依赖
  4. Repository 模式:Domain 层定义接口,Data 层实现,实现依赖倒置
  5. 模块化:按功能分层(:domain:data:presentation),提升编译速度

优势对比

特性传统 MVC/MVPClean Architecture
业务逻辑独立性与 UI/框架耦合完全独立,可跨平台复用
可测试性需 Android 环境Domain 层纯单元测试,无需模拟器
技术栈迁移重构成本高只需替换外层适配器
维护成本随时间递增长期稳定,适应变化

QA

Q:MVC 也是一种 Clean Architecture?

A:MVC 是“平面分离”,Clean Architecture 是“分层嵌套”

  • MVC 的结构:
用户 → Controller → Model → Database  
     ↑            ↓  
     View ← (数据展示)
- Model、View、Controller 是平级关系,直接相互调用(例如 Controller 可能直接操作 Model,View 可能直接读取 Model)。
- 问题:如果 View 直接访问 Model,或 Controller 包含太多业务逻辑,会导致耦合度高,修改一处可能影响其他部分。
  • Clean Architecture 的结构:
用户 → UI Layer (如 Web/App)  

Interface Adapters (如 Controller/Presenter)  

Use Cases (业务逻辑)  

Entities (核心业务模型)
- 分层依赖:外层依赖内层,内层不知道外层的存在(例如 Use Cases 只依赖 Entities,不依赖 UI 或数据库)。
- 依赖倒置:通过接口(如`UserRepository`)隔离变化,外层实现接口,内层调用接口。
- 优势:修改 UI 或数据库时,只要接口不变,核心业务逻辑无需改动。

Q:怎么才算是怎么才算 Clean Architecture?

A:一个系统被认为是 Clean Architecture,通常需要满足以下特征(可看作“验收标准”):

  • ✅ 业务规则不依赖任何外部框架:你的核心代码(Entities、Use Cases)不能出现 import 任何数据库驱动、Web 框架、第三方库(除标准库外)。
  • ✅ 依赖倒置:外层(如 Controller、Repository 实现)依赖内层定义的接口,而非具体实现。内层定义接口,外层提供实现。
  • ✅ 独立于 UI:用户界面可以随意更换(Web、命令行、移动 App),而不影响业务逻辑。
  • ✅ 独立于数据库:你可以随时更换数据库(SQL、NoSQL、文件系统),业务逻辑不变。
  • ✅ 独立于任何外部服务:第三方服务(如支付网关、消息队列)都被适配到外层,业务层通过接口调用它们。
  • ✅ 可测试性:你可以用简单的单元测试覆盖所有业务用例,无需启动数据库或 Web 服务器。
  • ✅ 清晰的边界:每一层都有明确定义的职责,层与层之间通过简单的数据结构或接口通信,没有“漏层”现象。

Q:Clean Architecture 和 DDD 什么区别?

A:

  • Clean Architecture 是一种架构模式,它关注的是如何组织代码的层次,以及如何控制依赖方向(内层不依赖外层)。它提供了一种将业务逻辑与技术细节分离的通用架构骨架。
  • 领域驱动设计(DDD) 是一种设计方法论,它关注的是如何对复杂的业务领域进行建模,并通过一系列战术模式(实体、值对象、聚合、仓储、领域服务、领域事件等)来表达业务逻辑。

两者可以完美结合:Clean Architecture 为 DDD 提供了理想的架构支撑——它的内层(实体、用例)正好可以容纳 DDD 的领域模型和应用层,外层负责技术细节。这样既能保持领域模型的纯粹性,又能获得架构的灵活性。

更新: 2026-03-08 22:25:27
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/hql3g409nunpn0ql


相关笔记