整洁架构三连问:是什么,怎么做,为什么要用

整洁架构问答

Q1:什么是整洁架构?

由 Robert C. Martin(Uncle Bob)提出,核心思想:业务逻辑独立于框架、UI、数据库等外部细节,依赖关系只能从外层指向内层。

四层结构

swift 复制代码
┌─────────────────────────────────┐
│   Frameworks & Drivers          │  ← Web、数据库、UI(最易变)
│   ┌─────────────────────────┐   │
│   │  Interface Adapters     │   │  ← 控制器、网关、Presenter
│   │  ┌───────────────────┐  │   │
│   │  │  Application      │  │   │  ← 用例(Use Cases)
│   │  │  Business Rules   │  │   │
│   │  │  ┌─────────────┐ │  │   │
│   │  │  │ Enterprise  │ │  │   │  ← 实体(Entities)最稳定
│   │  │  │ Business    │ │  │   │
│   │  │  │ Rules       │ │  │   │
│   │  │  └─────────────┘ │  │   │
│   │  └───────────────────┘  │   │
│   └─────────────────────────┘   │
└─────────────────────────────────┘
  1. 实体层:核心业务规则,零依赖
  2. 用例层:编排实体完成应用逻辑
  3. 接口适配层:数据转换,连接内外
  4. 框架与驱动层:具体技术实现

关键收益

  • 可测试:业务逻辑不依赖外部,独立单元测试
  • 可替换:换数据库/框架不影响核心逻辑
  • 可维护:修改外层不影响内层

一句话:让业务逻辑成为核心,让数据库、框架等成为可插拔的细节。


Q2:整洁架构和普通架构有什么区别?

普通架构 按技术类型分层,整洁架构按依赖方向分层。

直观对比

普通架构(传统分层)
css 复制代码
src/
├── components/    ← UI 组件
├── api/           ← 接口调用
├── store/         ← 状态管理
├── utils/         ← 工具函数
└── views/         ← 页面

依赖方向:随意交叉

复制代码
组件 ←→ store ←→ api
 ↕        ↕       ↕
utils ←─ views ←─ components
整洁架构
css 复制代码
src/
├── domain/           ← 纯业务(零依赖)
├── application/      ← 用例编排
├── infrastructure/   ← 技术实现
└── ui/               ← 界面渲染

依赖方向:只从外向内

markdown 复制代码
ui → application → domain
infrastructure → application → domain
         ↘          ↓          ↗
          任何外层都可以依赖内层
          内层永远不知道外层存在

具体差异对比

维度 普通架构 整洁架构
分层依据 按技术类型(组件、API、工具) 按依赖方向(外层依赖内层)
业务逻辑位置 散落在组件、store、utils 集中在 domain 层
框架耦合 业务逻辑依赖 Vue/React domain 层零框架依赖
改数据库 改 api/ + store/ + 组件 只改 infrastructure/
换框架 几乎重写 只重写 ui/ 层
单元测试 需要 mock 框架、mount 组件 直接测纯 TS 函数
API 字段变了 改多处 只改 Repository 转换逻辑

代码对比:同一个需求

普通架构写法
javascript 复制代码
// 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 }
})
xml 复制代码
<!-- CartPage.vue -->
<script setup>
import { useCartStore } from '@/store/cart'

const cartStore = useCartStore()

// 组件里也有业务逻辑
const canAdd = computed(() => cartStore.items.length < 20)
</script>

问题:业务规则在 store 和组件里都有,换框架全废,换 axios 要改 store。

整洁架构写法
arduino 复制代码
// domain/aggregates/Cart.ts --- 纯 TS,零依赖
export class Cart {
  private items: CartItem[] = []

  addItem(item: CartItem): void { /* 规则在这里 */ }
  canAddMore(): boolean { return this.items.length < 20 }
}
typescript 复制代码
// 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)   // 持久化
  }
}
typescript 复制代码
// infrastructure/repositories/ApiCartRepository.ts --- 适配
export class ApiCartRepository implements CartRepository {
  async getCart() { /* axios → 领域对象 */ }
  async save(cart) { /* 领域对象 → axios */ }
}
xml 复制代码
<!-- 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 默认结构

css 复制代码
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 组件的问题

xml 复制代码
<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 不会自动帮你分层:

bash 复制代码
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 是脚手架工具 ,只管项目初始化和构建配置,不管架构分层。默认生成的项目是传统分层(按技术类型分),不是整洁架构(按依赖方向分)。整洁架构需要开发者自己设计和实施。

相关推荐
一个处女座的程序猿O(∩_∩)O2 小时前
React 完全入门指南:从基础概念到组件协作
前端·react.js·前端框架
前端摸鱼匠2 小时前
Vue 3 的defineEmits编译器宏:详解<script setup>中defineEmits的使用
前端·javascript·vue.js·前端框架·ecmascript
里欧跑得慢3 小时前
Flutter 测试全攻略:从单元测试到集成测试的完整实践
前端·css·flutter·web
Jagger_3 小时前
前端整洁架构详解
前端
徐小夕3 小时前
我花一天时间Vibe Coding的开源AI工具,一键检测你的电脑能跑哪些AI大模型
前端·javascript·github
英俊潇洒美少年3 小时前
Vue3 企业级封装:useEventListener + 终极版 BaseEcharts 组件
前端·javascript·vue.js
嵌入式×边缘AI:打怪升级日志3 小时前
使用JsonRPC实现前后台
前端·后端
小码哥_常4 小时前
深度剖析:为什么Android选择了Binder
前端
方安乐5 小时前
单元测试之helper函数
前端·javascript·单元测试