随着前端项目规模的不断增长,"如何组织代码"这个问题变得越来越重要。
不再只是简单的 CRUD 页面,我们开始面对复杂的状态管理、多数据源、
频繁变更的业务逻辑——这时候,一个好的架构就是项目的生命线。
Clean Architecture(整洁架构)由 Robert C. Martin(Uncle Bob)提出,
核心理念是关注点分离和依赖反转。
虽然它最初是为后端设计的,但前端同样可以从中受益。
Clean Architecture 的核心原则
简单来说,Clean Architecture 把代码分成几层,每一层都有明确的职责,
依赖关系从外向内——外层依赖内层,内层对外层一无所知。
- 实体(Entities): 最内层,包含业务核心概念和规则,不依赖任何框架。
- 用例(Use Cases): 应用特定的业务逻辑,编排实体的使用流程。
- 接口适配(Interface Adapters): 把用例的输出适配到外部框架(如 React/Vue)。
- 框架与驱动(Frameworks & Drivers): 最外层,UI 框架、API 客户端、数据库等。
关键原则: 依赖规则(Dependency Rule)—— 源代码的依赖只能指向内层,
内层永远不知道外层的任何信息。
在前端项目中如何实践
以一个典型的 React 项目为例,我们可以这样组织目录结构:
src/
├── core/ # 最内层:业务实体
│ ├── entities/
│ │ └── User.ts
│ └── value-objects/
│ └── Email.ts
├── application/ # 用例层
│ ├── use-cases/
│ │ ├── CreateUser.ts
│ │ └── GetUserProfile.ts
│ └── ports/ # 接口定义
│ ├── UserRepository.ts
│ └── AuthService.ts
├── infrastructure/ # 外部实现
│ ├── api/
│ │ └── UserApi.ts
│ └── storage/
│ └── LocalStorage.ts
├── presentation/ # UI 层
│ ├── components/
│ ├── pages/
│ └── hooks/
└── di/ # 依赖注入
└── container.ts
这个结构的好处是什么?
- 业务逻辑独立:
core和application层的代码
完全不依赖 React,可以单独测试。 - 替换外部依赖简单: 今天用 REST API,明天想换 GraphQL?
只需要在infrastructure层替换实现,核心逻辑不用动。 - 测试友好: 用例层的测试不需要模拟 DOM 或浏览器 API,
纯 JavaScript 就能跑。
一个简单的例子
假设我们有一个"获取用户信息"的功能。传统的做法可能直接在组件里调用 API:
// ❌ Bad: 业务逻辑直接写在 UI 层
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setUser)
}, [userId])
// ...
}
用 Clean Architecture 的思路改写:
// core/entities/User.ts
class User {
constructor(
public readonly id: string,
public readonly name: string,
public readonly email: Email
) {}
}
// application/ports/UserRepository.ts
interface UserRepository {
findById(id: string): Promise<User>
}
// application/use-cases/GetUserProfile.ts
class GetUserProfile {
constructor(private userRepo: UserRepository) {}
async execute(userId: string): Promise<User> {
const user = await this.userRepo.findById(userId)
if (!user) throw new Error('User not found')
return user
}
}
// infrastructure/api/UserApi.ts
class UserApi implements UserRepository {
async findById(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`)
const data = await res.json()
return new User(data.id, data.name, new Email(data.email))
}
}
注意事项
虽然 Clean Architecture 有很多优点,但也需要注意不要过度设计:
- 小项目不必强求: 如果只是一个简单的 CRUD 页面,
分层反而增加了复杂度。等业务逻辑复杂后再"重构进去"也不迟。 - 团队共识很重要: 架构需要整个团队认同和理解。
如果只有一个人遵守规则,其他人直接跳过分层,反而会更混乱。 - 灵活调整: 不必照搬后端的四层结构。
前端可以简化为三层(领域层、应用层、展示层),根据自己的场景裁剪。
总结
Clean Architecture 提供了一种有价值的设计思路,但并不是万能的银弹。
它的核心价值在于让业务逻辑保持干净和可测试,这在项目规模变大时的收益非常明显。
关键在于:什么时候用、用多少,需要根据项目的实际情况来判断。
架构设计本身就是一个不断权衡的过程。