Skip to content

系统架构:前端架构的本质与演进


核心要点(TL;DR)

  • 前端架构的本质是组织代码、应对变化与平衡性能/效率/可维护性,框架只是工具而非架构本身。
  • MVC/MVP/MVVM 的核心都是分离界面与业务逻辑,MVVM 通过响应式系统实现 View 与 ViewModel 自动同步。
  • BFF 在前后端之间提供面向前端的适配层,负责接口聚合、数据适配、鉴权与缓存。
  • DDD 思想可帮助前端按领域拆分模块、定义领域模型并通过防腐层隔离后端 DTO 变化。
  • 分层架构、模块化、依赖倒置与架构决策记录(ADR)是控制复杂度与可维护性的关键实践。
  • 现代前端架构模式包括:微内核、插件化、低代码/搭建、Server-Driven UI、事件驱动/CQRS、六边形/整洁架构等。
  • 架构模式选型应基于业务场景、团队能力、可维护性和演进成本,使用决策树和 ADR 记录决策过程。

学习时长与前置知识

  • 建议学习时长:4-6 周(每周投入 6-8 小时)
  • 前置知识:前端工程化、框架原理、BFF、设计模式基础

一、前端架构到底是什么?

如果把一个 Web 应用比作一座城市,那么 HTML/CSS/JavaScript 就是砖瓦和钢筋,而前端架构则是这座城市的规划图:道路如何连接、区域如何划分、水电如何布线、未来如何扩建。没有好的架构,城市会陷入“摊大饼”式的混乱;没有好的前端架构,项目会在需求迭代中逐渐失控。

前端架构的本质,可以概括为三句话:

  1. 组织代码的结构:让代码有清晰的边界,便于多人协作和长期维护。
  2. 应对变化的策略:业务在变、技术在变、团队在变,好的架构让变化成本最低。
  3. 平衡的艺术:在性能、可维护性、开发效率、可扩展性之间找到最佳平衡点。

很多初学者把“会用 Vue/React/Angular”等同于“懂前端架构”,这是一个巨大误区。框架只是工具,架构是思想和组织方式。就像会用 Photoshop 不等于会设计建筑。


二、前端架构的演进:从“脚本时代”到“工程化时代”

1. 混沌期:jQuery 一统天下(2006-2013)

这个时代的前端代码通常长这样:

javascript
$(document).ready(function () {
  $('#btn').click(function () {
    $.ajax('/api/user', {
      success: function (data) {
        $('#name').text(data.name);
      }
    });
  });
});

代码按页面组织,业务逻辑、DOM 操作、网络请求混在一起。项目小的时候很爽,大了之后就是“意大利面条”。

痛点:全局变量污染、代码复用困难、测试困难、协作困难。

2. 模块化时代:RequireJS / SeaJS / AMD / CommonJS

为了解决代码组织问题,社区提出了模块化规范。代码被拆成一个个模块:

javascript
// utils.js
define(function () {
  return {
    formatDate: function (date) { /* ... */ }
  };
});

这相当于把城市从“大院混杂”改成了“小区制”,每个小区有明确边界。

3. 组件化时代:MV* 框架兴起

React、Vue、Angular 把界面拆成组件。一个组件包含自己的结构、样式、逻辑:

jsx
function UserCard({ user }) {
  return (
    <div className="user-card">
      <img src={user.avatar} />
      <h3>{user.name}</h3>
    </div>
  );
}

组件化让 UI 开发从“雕刻整幅画”变成“拼装乐高积木”。

4. 工程化与架构化时代

现代前端不仅要写代码,还要考虑:

  • 构建与部署:Webpack/Vite/Rollup
  • 状态管理:Redux/Vuex/Pinia/Zustand
  • 路由与导航:React Router/Vue Router
  • 测试与质量:Jest/Vitest/Cypress
  • 性能与体验:代码分割、懒加载、SSR/SSG
  • 跨团队协作:微前端、Monorepo

前端架构已经从一个“写页面的岗位”演变为“复杂客户端系统的架构设计”。


三、MVC / MVP / MVVM 在前端的体现

这三种模式的核心目标一致:把界面(View)和业务逻辑(Model)分开,只是分法不同。

1. MVC:Model-View-Controller

经典分层:

用户操作 → Controller → 更新 Model → View 刷新

在前端早期,Backbone.js 是 MVC 的代表:

javascript
// Model
var User = Backbone.Model.extend({
  defaults: { name: '' }
});

// View
var UserView = Backbone.View.extend({
  render: function () {
    this.$el.html('<p>' + this.model.get('name') + '</p>');
    return this;
  }
});

// Controller 通常由路由承担
var Router = Backbone.Router.extend({
  routes: { 'user/:id': 'showUser' },
  showUser: function (id) { /* ... */ }
});

生活化比喻:MVC 像餐厅。Model 是后厨(数据和业务),View 是菜单和摆盘(界面),Controller 是服务员(接收顾客指令并协调后厨与摆盘)。

常见误区:很多人把 React + Redux 叫 MVC,其实 Redux 更像 Flux 架构,不是严格 MVC。

2. MVP:Model-View-Presenter

Presenter 是 View 和 Model 之间的中间人。View 只负责显示,所有逻辑都在 Presenter:

View ↔ Presenter ↔ Model

在前端,MVP 常见于需要高度测试隔离的场景,比如原生 Android 开发转前端的团队。

优点:View 极其单薄,容易单元测试。 缺点:Presenter 容易变成“上帝类”。

3. MVVM:Model-View-ViewModel

MVVM 是前端最流行的模式。Vue 和 Angular 都是典型代表:

View <-> ViewModel <-> Model
     (双向绑定)
html
<!-- View -->
<div id="app">
  <p>&#123;&#123; message &#125;&#125;</p>
  <button @click="reverseMessage">反转</button>
</div>
javascript
// ViewModel
new Vue({
  el: '#app',
  data: {
    message: 'Hello MVVM'
  },
  methods: {
    reverseMessage() {
      this.message = this.message.split('').reverse().join('');
    }
  }
});

本质:通过响应式系统或脏检查,让 View 和 ViewModel 自动同步。

生活化比喻:MVVM 像智能音箱。你对音箱说“播放音乐”(View 层操作),音箱自己调用音乐服务(ViewModel),播放歌曲(Model 更新),整个过程你不需要知道背后调用了哪些 API。

对比总结

模式核心思想前端代表适合场景
MVCController 协调 M 和 VBackbone中小型项目
MVPPresenter 承担所有逻辑自定义框架需要强测试隔离
MVVM双向绑定自动同步Vue、Angular数据驱动 UI

React 严格来说不是 MVVM,它是“函数式 UI = f(state)”,但通过 useState/useEffect 也能模拟出类似 MVVM 的开发体验。


四、BFF:Backend for Frontend 架构

1. 为什么需要 BFF?

假设后端有一个“订单服务”,返回的数据结构是通用的:

json
{
  "orderId": "123",
  "userId": "456",
  "items": [...],
  "createdAt": "2023-01-01T00:00:00Z",
  "statusCode": 1
}

但前端需要:

  • PC 端:展示完整订单详情
  • H5 端:只展示关键信息
  • 小程序:需要额外字段适配其组件规范

如果所有端都直接调用同一个后端 API,后端会被迫为不同端定制接口,导致耦合严重。

2. BFF 的解决方案

在前后端之间加一层“面向前端的后端”:

PC Web  →  PC-BFF  →
H5      →  H5-BFF  →  通用后端服务(订单/用户/支付)
小程序   →  小程序-BFF →

BFF 层负责:

  • 接口聚合:一次请求聚合多个后端服务
  • 数据适配:把后端数据转成前端需要的格式
  • 鉴权与限流:统一处理登录态、权限、流控
  • 缓存:针对前端场景做缓存策略
javascript
// H5-BFF /api/order-summary
app.get('/api/order-summary', async (req, res) => {
  const order = await orderService.get(req.query.id);
  const user = await userService.get(order.userId);

  res.json({
    title: `${user.name} 的订单`,
    totalCount: order.items.length,
    totalPrice: order.items.reduce((sum, item) => sum + item.price, 0),
    statusText: STATUS_MAP[order.statusCode]
  });
});

3. BFF 的常见误区

  • 误区 1:BFF 只是“API 转发”。错,它的价值在于聚合和适配。
  • 误区 2:一个前端团队一个 BFF。错,应该按业务领域或端来划分。
  • 误区 3:所有请求都走 BFF。错,通用、稳定的接口可以直接调用。

生活化比喻:BFF 像餐厅的点餐台。后厨(通用后端)只负责做菜,顾客(不同端)需求不同,点餐台(BFF)负责把顾客需求汇总、翻译成后厨能理解的订单。


五、DDD:领域驱动设计在前端的应用

DDD(Domain-Driven Design)原本用于后端复杂业务系统,但前端同样可以借鉴其思想。

1. DDD 的核心理念

  • 领域(Domain):业务问题空间,比如电商中的“订单领域”“库存领域”。
  • 实体(Entity):有唯一标识的对象,如 OrderUser
  • 值对象(Value Object):无唯一标识,如 MoneyAddress
  • 聚合(Aggregate):一组相关对象的集合,由聚合根统一对外。
  • 限界上下文(Bounded Context):明确业务边界,不同上下文用不同模型。

2. DDD 在前端的落地

前端通常不是业务逻辑的核心载体,但可以在以下方面应用 DDD:

(1)按领域拆分模块

不要按技术类型拆分(components/services/utils),而是按业务领域拆分:

src/
  order/
    components/
    services/
    stores/
    types/
    utils/
  product/
    components/
    services/
    stores/
    types/
    utils/
  user/
    ...

这种结构让“订单相关的所有东西”都在 order/ 目录下,便于定位和维护。

(2)定义领域模型

typescript
// domain/order.ts
export class Order {
  constructor(
    public id: string,
    public items: OrderItem[],
    public status: OrderStatus
  ) {}

  get totalPrice(): number {
    return this.items.reduce((sum, item) => sum + item.subtotal, 0);
  }

  canCancel(): boolean {
    return this.status === OrderStatus.PENDING;
  }
}

export class OrderItem {
  constructor(
    public productId: string,
    public quantity: number,
    public unitPrice: number
  ) {}

  get subtotal(): number {
    return this.quantity * this.unitPrice;
  }
}

(3)防腐层(Anti-Corruption Layer)

当后端接口设计不符合前端领域模型时,用防腐层做转换:

typescript
// adapters/orderAdapter.ts
export function adaptOrderFromDTO(dto: OrderDTO): Order {
  return new Order(
    dto.order_id,
    dto.items.map(item => new OrderItem(item.product_id, item.qty, item.price)),
    STATUS_MAP[dto.status_code]
  );
}

这样,业务代码只依赖 Order 领域模型,不依赖后端 DTO 结构。当后端接口变化时,只需改防腐层。

生活化比喻:DDD 像医院的分科室。内科、外科、儿科各自有专业术语和流程(限界上下文),但对外提供统一的挂号入口(防腐层)。患者不需要懂医学术语,只需要按症状挂号。

3. 前端 DDD 的边界

前端不要过度设计。DDD 适合复杂业务系统,简单 CRUD 项目用 DDD 反而增加成本。判断标准是:业务规则是否复杂?是否有多个团队在同一代码库协作?模型是否频繁变化?


六、分层架构、模块化、依赖管理与可扩展性

1. 分层架构

经典分层:

表现层(Presentation Layer)

业务逻辑层(Business Logic Layer)

数据访问层(Data Access Layer)

外部服务(API / Storage)

在前端中,可以对应为:

UI 组件层

状态/业务逻辑层(Store / Services)

数据访问层(API Client / Adapter)

后端 API

关键原则:上层可以依赖下层,下层不能依赖上层。

2. 模块化

模块化的目标:高内聚、低耦合

  • 高内聚:相关代码放在一起。
  • 低耦合:模块之间通过明确接口交互,不依赖内部实现。
typescript
// 好的接口:只暴露必要的东西
export function useOrder() { /* ... */ }
export type { Order } from './types';

// 坏的接口:暴露内部实现细节
export { internalFetchOrder } from './api';
export { orderReducer } from './reducer';

3. 依赖管理

前端依赖管理不仅是 npm 包,还包括模块之间的依赖关系。

依赖倒置原则(DIP):高层模块不应该依赖低层模块的具体实现,而应该依赖抽象。

typescript
// 抽象接口
interface Logger {
  log(message: string): void;
}

// 具体实现可以替换
class ConsoleLogger implements Logger { /* ... */ }
class SentryLogger implements Logger { /* ... */ }

// 业务代码依赖 Logger 接口,不依赖具体实现
class OrderService {
  constructor(private logger: Logger) {}
}

生活化比喻:依赖倒置像插头插座。电器(高层模块)依赖插座标准(抽象接口),不依赖发电厂(具体实现)。你可以换发电厂、换电池,只要插座标准不变。

4. 可扩展性设计

可扩展性不是“预留一切”,而是“在不修改现有代码的前提下增加新功能”。这通常依赖:

  • 插件化架构:通过约定接口让功能可插拔
  • 策略模式:把变化的行为封装成策略
  • 事件总线 / 发布订阅:解耦模块间通信
typescript
// 插件化示例
const plugins = [];

export function registerPlugin(plugin) {
  plugins.push(plugin);
}

export function onRouteChange(route) {
  plugins.forEach(plugin => plugin.onRouteChange?.(route));
}

七、现代前端架构模式

传统 MVC/MVVM、BFF、DDD 是前端架构的基础,但随着业务复杂度提升,更多现代架构模式涌现。掌握这些模式,能让我们在特定场景下做出更优的架构决策。

1. 微内核架构(Microkernel / Plugin Architecture)

核心思想

将系统拆分为一个最小核心(Kernel)和一组可插拔的插件(Plugins)。核心负责生命周期管理、模块通信和基础服务;插件负责具体业务功能。

┌─────────────────────────────────────┐
│           Application               │
├─────────────────────────────────────┤
│  Plugin A   │  Plugin B  │ Plugin C │
├─────────────────────────────────────┤
│         Plugin Manager              │
├─────────────────────────────────────┤
│  Kernel(生命周期、事件总线、依赖注入)│
└─────────────────────────────────────┘

前端应用场景

  • IDE / 低代码平台:VS Code、Figma 插件生态。
  • 企业级中台:不同业务线以插件形式接入统一基座。
  • 微前端的一种实现方式:基座作为 Kernel,子应用作为 Plugin。

简单实现示例

typescript
// kernel.ts
class Kernel {
  private plugins = new Map<string, Plugin>();
  private eventBus = new EventTarget();

  register(name: string, plugin: Plugin) {
    this.plugins.set(name, plugin);
    plugin.activate(this.eventBus);
  }

  unregister(name: string) {
    const plugin = this.plugins.get(name);
    plugin?.deactivate();
    this.plugins.delete(name);
  }
}

interface Plugin {
  activate(eventBus: EventTarget): void;
  deactivate(): void;
}

优缺点

优点缺点
功能可独立开发、部署、卸载插件接口设计难度大
系统核心稳定,扩展灵活插件间通信和依赖管理复杂
适合多团队协作需要完善的安全隔离机制

2. 插件化架构的进阶:模块联邦与动态加载

除了自己实现插件系统,现代前端还可以借助:

  • Module Federation:运行时共享和加载远程模块。
  • ESM Dynamic Import:按需加载业务模块。
  • Web Components:用标准组件封装插件 UI。
typescript
// 动态加载远程模块作为插件
async function loadPlugin(url: string) {
  const module = await import(/* @vite-ignore */ url);
  return module.default;
}

3. 低代码 / 搭建体系(Low-Code / Schema-Driven UI)

核心思想

配置(Schema)描述界面,而不是直接写代码。渲染引擎解析 Schema 并生成真实 UI。

用户拖拽/自然语言 → 生成 Schema → 渲染引擎 → 真实页面

典型 Schema 示例

json
{
  "type": "Page",
  "props": { "title": "销售数据看板" },
  "children": [
    {
      "type": "Chart",
      "props": { "chartType": "line", "dataSource": "salesTrend" }
    },
    {
      "type": "Table",
      "props": { "columns": ["商品", "销量", "金额"], "dataSource": "topProducts" }
    }
  ]
}

前端架构要点

层面职责关键技术
物料层组件库、图表库、模板React/Vue 组件
协议层Schema 定义、版本管理JSON Schema、Proprietary DSL
渲染层解析 Schema 并渲染递归渲染器、动态组件映射
数据层数据源绑定、状态管理变量引擎、API 编排
权限层组件级/字段级权限权限表达式

适用场景

  • 运营后台、数据看板、活动页搭建。
  • 表单、列表、报表等高度模式化的页面。
  • 需要业务人员自助配置的场景。

4. Server-Driven UI(SDUI)

核心思想

服务端不仅提供数据,还提供界面结构。客户端根据服务端下发的 UI 描述动态渲染。

传统方式:服务端返回数据,前端决定如何展示。
SDUI:服务端返回“数据 + 界面结构”,前端只负责渲染。

典型应用场景

  • 电商首页:不同活动、不同用户看到不同模块组合。
  • 金融 App:合规要求下,页面结构需服务端动态控制。
  • A/B 测试:服务端下发不同 UI 版本,无需发版即可实验。

与低代码的关系

维度低代码平台SDUI
控制方运营/产品经理通过编辑器配置服务端根据业务逻辑动态下发
灵活性高(可拖拽设计)中(受限于预定义组件)
实时性配置后发布生效请求时即时生效
适用后台系统、活动页C 端动态页面、首页、卡片流

前端实现要点

  1. 组件映射表:将服务端组件类型映射到本地组件。
  2. 版本兼容:服务端 Schema 升级时,旧版本客户端能兼容。
  3. 降级策略:未知组件类型时展示占位或跳过。
  4. 性能优化:对常用 Schema 做缓存和预编译。

5. 事件驱动架构(Event-Driven Architecture)

核心思想

模块之间不直接调用,而是通过事件总线发布和订阅事件来通信。

typescript
// 发布事件
eventBus.emit('order:created', { orderId: '123' });

// 订阅事件
eventBus.on('order:created', ({ orderId }) => {
  analytics.track('purchase', { orderId });
});

前端应用场景

  • 跨模块通信:订单模块创建订单后,通知购物车、优惠券、埋点模块。
  • Undo/Redo:用事件溯源实现操作历史。
  • 复杂 UI 流程:表单校验、审批流、多步骤向导。

与 CQRS / 事件溯源的关系

模式核心思想前端应用
事件驱动通过事件解耦模块跨模块通信、埋点
CQRS读模型和写模型分离复杂列表/详情页的状态优化
事件溯源用事件序列表示状态Undo/Redo、操作回放
typescript
// 事件溯源示例:用命令和事件管理状态
interface Command {
  type: 'ADD_TODO' | 'REMOVE_TODO';
  payload: any;
}

interface Event {
  type: 'TODO_ADDED' | 'TODO_REMOVED';
  payload: any;
}

function reducer(events: Event[], command: Command): Event[] {
  switch (command.type) {
    case 'ADD_TODO':
      return [...events, { type: 'TODO_ADDED', payload: command.payload }];
    // ...
  }
}

6. 六边形架构 / 整洁架构在前端

核心思想

业务逻辑独立于框架、UI 和外部服务。通过端口(Port)和适配器(Adapter)与外部交互。

         ┌─────────────┐
         │     UI      │
         └──────┬──────┘
                │ 适配器
         ┌──────▼──────┐
         │  应用层/业务 │
         │    逻辑      │
         └──────┬──────┘
                │ 适配器
         ┌──────▼──────┐
         │  外部服务    │
         │ (API/Storage)│
         └─────────────┘

前端落地示例

typescript
// domain/todo.ts —— 核心业务逻辑,不依赖任何框架
export class Todo {
  constructor(public id: string, public title: string, public completed = false) {}

  toggle() {
    this.completed = !this.completed;
  }
}

// ports/todoRepository.ts —— 抽象端口
export interface TodoRepository {
  findAll(): Promise<Todo[]>;
  save(todo: Todo): Promise<void>;
}

// adapters/apiTodoRepository.ts —— 具体适配器
export class ApiTodoRepository implements TodoRepository {
  async findAll() {
    const dtos = await fetch('/api/todos').then(r => r.json());
    return dtos.map(dto => new Todo(dto.id, dto.title, dto.completed));
  }
  // ...
}

// UI 层只依赖应用层和端口,不依赖具体适配器
function TodoList({ repository }: { repository: TodoRepository }) {
  // ...
}

价值

  • 可测试性:业务逻辑可以脱离浏览器和 API 单独测试。
  • 可替换性:今天用 REST API,明天可以换成 GraphQL 或本地缓存,业务逻辑不变。
  • 长期可维护:框架更新换代时,领域层代码可以复用。

7. Monorepo 组织方案

Monorepo 不仅是代码托管方式,也是一种架构组织方式。不同组织方式对应不同的团队协作模式:

组织方式结构适用场景
按领域划分packages/orderpackages/product业务复杂、团队按领域负责
按产品划分apps/webapps/adminapps/mobile多端产品、团队按产品负责
按技术栈划分packages/uipackages/utilspackages/api技术平台型团队
混合划分apps/ + packages/中大型团队,兼顾产品和技术复用

选择依据

  • 团队规模与组织结构(康威定律)。
  • 代码复用需求。
  • 构建和部署复杂度。
  • 发布节奏是否一致。

8. 架构模式选型决策树

面对一个具体业务场景,如何快速选择合适的架构模式?可以按以下问题逐步判断:

1. 业务是否高度模式化、需要频繁配置?
   ├─ 是 → 低代码/搭建体系
   └─ 否 → 继续判断

2. 页面结构是否需要服务端动态控制、无需发版即可调整?
   ├─ 是 → Server-Driven UI
   └─ 否 → 继续判断

3. 是否需要多个独立团队/业务线在一个系统中并行开发?
   ├─ 是 → 微内核/插件化 或 微前端
   └─ 否 → 继续判断

4. 业务规则是否复杂、需要长期演进和大量测试?
   ├─ 是 → DDD + 六边形/整洁架构
   └─ 否 → 继续判断

5. 模块间通信是否复杂、需要解耦和可追溯?
   ├─ 是 → 事件驱动/CQRS/事件溯源
   └─ 否 → 经典分层架构 + 模块化

架构选型评分卡

维度权重说明
业务契合度30%是否能自然表达业务结构
团队能力20%团队是否具备实施和维护能力
可维护性20%长期迭代成本
性能影响15%对首屏、交互性能的影响
生态成熟度15%工具链、社区、案例丰富度

建议将选型过程和评分结果记录到 ADR 中,便于后续回顾。


八、常见误区与最佳实践

误区 1:架构越复杂越好

很多团队看到大厂的架构就照搬。其实,架构应该匹配团队规模和业务复杂度。小团队用微服务、微前端,就像骑自行车开 NASCAR,反而拖累效率。

误区 2:只关注技术,不关注组织

康威定律说:“设计系统的组织,其产生的设计等价于组织间的沟通结构。” 好的架构必须匹配团队结构。如果团队按业务领域划分,代码也应该按领域划分。

误区 3:过度抽象

抽象是为了应对变化,但过度抽象会增加理解成本。原则是:先简单,后复杂;先复制,后抽象。不要一开始就为“未来可能的变化”写一堆接口。

最佳实践

  1. 单一职责原则:一个模块只做一件事。
  2. 开闭原则:对扩展开放,对修改关闭。
  3. 依赖倒置:依赖抽象,不依赖具体实现。
  4. 显式优于隐式:配置和约定要清晰可追踪。
  5. 可测试性驱动设计:如果一个模块难以测试,通常意味着架构有问题。
  6. 持续重构:架构不是一次性设计出来的,是演进出来的。
  7. 用决策树和评分卡辅助架构选型:避免盲目跟风,把选型过程记录为 ADR。
  8. 根据业务场景选择现代架构模式:低代码、SDUI、微内核、事件驱动、整洁架构各有适用场景。

九、架构决策记录与可维护性

1. 为什么需要记录架构决策?

很多团队在架构演进中会遇到一个经典问题:新加入的同事问“为什么当初要这样设计?”时,没有人能回答清楚。 architecture decision record(ADR)就是用来解决这个问题的。

ADR 是一份简短文档,记录某个重要的架构决策:

  • 当时面临什么问题?
  • 有哪些备选方案?
  • 为什么选择了当前方案?
  • 带来了哪些 trade-off?
markdown
# ADR-001:采用 BFF 架构

## 背景
前后端直接交互导致多端接口耦合严重。

## 备选方案
1. 后端为每端单独提供接口:加重后端负担。
2. 前端自行聚合:逻辑分散,重复请求多。
3. 引入 BFF:统一适配层。

## 决策
采用方案 3,按端建立 BFF 服务。

## Trade-off
增加了服务维护成本,但降低了前后端耦合。

记录 ADR 不是为了形式,而是让架构决策可追溯、可讨论、可改进。

2. 架构的可维护性如何度量?

可维护性没有统一公式,但可以通过以下指标观察:

  • 变更成本:新增一个功能需要改多少文件?
  • Bug 回归率:改 A 功能是否经常影响 B 功能?
  • 新人上手时间:新成员多久能独立开发?
  • 测试覆盖率:核心路径是否有测试保护?
  • 代码耦合度:模块间依赖是否清晰?

一个可维护的系统,应该让“小改动”真的只需要“小改动”。


十、前端与后端的协作边界

前端架构不是孤立的,它必须与后端架构协同。常见的协作边界问题包括:

  1. 接口设计:谁定义接口?RESTful 还是 GraphQL?是否需要 BFF?
  2. 数据模型:前后端是否使用同一套领域语言?
  3. 错误处理:后端返回什么状态码?错误信息如何展示?
  4. 鉴权与权限:登录态由谁维护?权限如何下发?
  5. 缓存策略:哪些数据可以缓存?缓存多久?

解决这些问题的关键是:早期对齐、文档化、契约化

推荐做法:

  • 前后端共同维护 API 文档(如 Swagger、OpenAPI)。
  • 关键接口做契约测试。
  • 建立联合评审机制,重大变更需双方确认。
  • 使用 BFF 或防腐层隔离变化。

生活化比喻:前后端像餐厅的前厅和后厨。前厅(前端)负责接待顾客、展示菜品,后厨(后端)负责做菜。双方需要通过菜单(API)和点单系统(BFF)协作,否则上错菜、等太久都会让顾客不满。


十一、总结

前端架构不是炫技,而是为了解决复杂系统在多人协作、快速迭代、长期演进中的问题。从 MVC 到 MVVM,从模块化到组件化,从 BFF 到 DDD,背后贯穿的是同一个思想:把变化隔离、把职责分离、把复杂度控制在可理解的范围内

作为前端架构师,你需要同时具备三种视角:

  • 工程师视角:代码如何组织、如何测试、如何部署。
  • 产品经理视角:业务如何拆分、需求如何落地、用户体验如何保障。
  • 组织视角:团队如何协作、边界如何划分、知识如何传承。

只有这三种视角结合,才能设计出既优雅又实用的前端架构。


领域编号:A01 系统架构设计
最后更新:2026-06-24


本领域学习进度

学习进度0 / 43 (0%)

基于 MIT 协议发布