整洁架构问答
Q1:什么是整洁架构?
由 Robert C. Martin(Uncle Bob)提出,核心思想:业务逻辑独立于框架、UI、数据库等外部细节,依赖关系只能从外层指向内层。
四层结构
┌─────────────────────────────────┐
│ Frameworks & Drivers │ ← Web、数据库、UI(最易变)
│ ┌─────────────────────────┐ │
│ │ Interface Adapters │ │ ← 控制器、网关、Presenter
│ │ ┌───────────────────┐ │ │
│ │ │ Application │ │ │ ← 用例(Use Cases)
│ │ │ Business Rules │ │ │
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ Enterprise │ │ │ │ ← 实体(Entities)最稳定
│ │ │ │ Business │ │ │ │
│ │ │ │ Rules │ │ │ │
│ │ │ └─────────────┘ │ │ │
│ │ └───────────────────┘ │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
- 实体层:核心业务规则,零依赖
- 用例层:编排实体完成应用逻辑
- 接口适配层:数据转换,连接内外
- 框架与驱动层:具体技术实现
关键收益
- 可测试:业务逻辑不依赖外部,独立单元测试
- 可替换:换数据库/框架不影响核心逻辑
- 可维护:修改外层不影响内层
一句话:让业务逻辑成为核心,让数据库、框架等成为可插拔的细节。
Q2:整洁架构和普通架构有什么区别?
普通架构按技术类型分层,整洁架构按依赖方向分层。
直观对比
普通架构(传统分层)
src/
├── components/ ← UI 组件
├── api/ ← 接口调用
├── store/ ← 状态管理
├── utils/ ← 工具函数
└── views/ ← 页面
依赖方向:随意交叉
组件 ←→ store ←→ api
↕ ↕ ↕
utils ←─ views ←─ components
整洁架构
src/
├── domain/ ← 纯业务(零依赖)
├── application/ ← 用例编排
├── infrastructure/ ← 技术实现
└── ui/ ← 界面渲染
依赖方向:只从外向内
ui → application → domain
infrastructure → application → domain
↘ ↓ ↗
任何外层都可以依赖内层
内层永远不知道外层存在
具体差异对比
| 维度 | 普通架构 | 整洁架构 |
|---|---|---|
| 分层依据 | 按技术类型(组件、API、工具) | 按依赖方向(外层依赖内层) |
| 业务逻辑位置 | 散落在组件、store、utils | 集中在 domain 层 |
| 框架耦合 | 业务逻辑依赖 Vue/React | domain 层零框架依赖 |
| 改数据库 | 改 api/ + store/ + 组件 | 只改 infrastructure/ |
| 换框架 | 几乎重写 | 只重写 ui/ 层 |
| 单元测试 | 需要 mock 框架、mount 组件 | 直接测纯 TS 函数 |
| API 字段变了 | 改多处 | 只改 Repository 转换逻辑 |
代码对比:同一个需求
普通架构写法
// store/cart.ts
import axios from 'axios'
export const useCartStore = defineStore('cart', () => {
const items = ref([])
async function addItem(productId: string) {
// 业务规则散落在 store 里
if (items.value.length >= 20) {
throw new Error('购物车已满')
}
// 直接耦合 axios
const res = await axios.post('/api/cart', { productId })
// 直接操作原始 JSON
items.value.push(res.data)
}
return { items, addItem }
})
<!-- CartPage.vue -->
<script setup>
import { useCartStore } from '@/store/cart'
const cartStore = useCartStore()
// 组件里也有业务逻辑
const canAdd = computed(() => cartStore.items.length < 20)
</script>
问题:业务规则在 store 和组件里都有,换框架全废,换 axios 要改 store。
整洁架构写法
// domain/aggregates/Cart.ts — 纯 TS,零依赖
export class Cart {
private items: CartItem[] = []
addItem(item: CartItem): void { /* 规则在这里 */ }
canAddMore(): boolean { return this.items.length < 20 }
}
// application/usecases/AddToCartUseCase.ts — 编排
export class AddToCartUseCase {
constructor(private repo: CartRepository) {}
async execute(id: string) {
const cart = await this.repo.getCart()
cart.addItem(item) // 调领域方法
await this.repo.save(cart) // 持久化
}
}
// infrastructure/repositories/ApiCartRepository.ts — 适配
export class ApiCartRepository implements CartRepository {
async getCart() { /* axios → 领域对象 */ }
async save(cart) { /* 领域对象 → axios */ }
}
<!-- ui/pages/CartPage.vue — 只渲染 -->
<script setup>
const { addItem } = useCart() // composable 调用用例
</script>
好处:业务规则只在 Cart.ts,换框架/换 axios 都不影响它。
什么时候用哪个
| 场景 | 建议 |
|---|---|
| 简单 CRUD、几个页面 | 普通架构够用,别过度设计 |
| 中等复杂度、有业务规则 | 提取 domain 层,用 composable 做应用层 |
| 复杂业务(交易、审批、计费) | 完整整洁架构 + DDD |
一句话总结:普通架构按技术分文件夹,整洁架构按依赖方向分层。前者简单但业务逻辑散落各处,后者前期成本高但业务逻辑内聚、可替换、可测试。
Q3:目前 Vue CLI 是不是都是整洁架构?
不是。 Vue CLI 生成的项目默认不符合整洁架构。
Vue CLI 默认结构
src/
├── components/ ← 组件(UI + 业务逻辑混在一起)
├── views/ ← 页面(UI + 业务逻辑混在一起)
├── router/ ← 路由
├── store/ ← Vuex/Pinia(状态管理)
├── api/ ← API 调用
├── utils/ ← 工具函数
└── App.vue
为什么不是整洁架构
| 整洁架构要求 | Vue CLI 默认 | 问题 |
|---|---|---|
| 依赖从外指向内 | 各层互相 import,方向混乱 | 无依赖规则约束 |
| 领域层纯 TS,零框架依赖 | 业务逻辑写在 .vue 里 | 领域层耦合了 Vue |
| 用例层编排领域对象 | 没有 use case 层 | 业务逻辑散落在组件和 store |
| Repository 接口在领域层定义 | 直接在组件里调 axios | 数据获取和 UI 耦合 |
典型 Vue 组件的问题:
<script setup>
// ❌ UI、业务规则、API 调用全混在一起
import axios from 'axios'
const cart = ref([])
async function addItem(id) {
if (cart.value.length >= 20) { // 业务规则在组件里
alert('购物车已满')
return
}
const res = await axios.post('/api/cart', { id }) // 直接调 API
cart.value.push(res.data)
}
</script>
Vue CLI 能改成整洁架构吗
能,但需要手动改造,Vue CLI 不会自动帮你分层:
src/
├── domain/ ← 手动添加:纯 TS 领域层
│ ├── value-objects/
│ ├── entities/
│ ├── aggregates/
│ └── repositories/ ← 接口定义
├── application/ ← 手动添加:用例层
│ ├── usecases/
│ └── composables/
├── infrastructure/ ← 手动添加:适配层
│ ├── repositories/ ← Repository 实现
│ └── api/
├── ui/ ← 原 components/views,只负责渲染
│ ├── components/
│ └── pages/
├── router/
└── App.vue
一句话总结
Vue CLI 是脚手架工具,只管项目初始化和构建配置,不管架构分层。默认生成的项目是传统分层(按技术类型分),不是整洁架构(按依赖方向分)。整洁架构需要开发者自己设计和实施。