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