编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 性能优化实践

  • 程序编程原理

  • 稳定性与可靠性

  • 工程化与运维

  • 方案设计思想

    • README
    • 架构与组件

      • 通用架构设计方案
        • 01.一个真实的事故
          • 1.1 事故背景还原
          • 1.2 复盘根因定位
          • 1.3 反思架构价值
        • 02.要解决什么问题
          • 2.1 复杂度的失控
          • 2.2 协作的边界
          • 2.3 演进的成本
          • 2.4 三个核心矛盾
        • 03.业界主流方案
          • 3.1 三种架构溯源
          • 3.2 横向对比矩阵
          • 3.3 代码结构对比
        • 04.架构设计原则
          • 4.1 三条优先级法则
          • 4.2 SOLID 落地解读
          • 4.3 分层依赖原则
        • 05.MVC 方案落地
          • 5.1 核心结构图
          • 5.2 数据流时序
          • 5.3 适用与不适用
        • 06.MVP 方案落地
          • 6.1 核心结构图
          • 6.2 数据流时序
          • 6.3 适用与不适用
        • 07.MVVM 方案落地
          • 7.1 核心结构图
          • 7.2 数据流时序
          • 7.3 适用与不适用
        • 08.架构设计陷阱
          • 8.1 过度设计反例
          • 8.2 欠缺设计反例
          • 8.3 选型错配反例
        • 09.架构演进路线
          • 9.1 V1 单体启动
          • 9.2 V2 分层规范
          • 9.3 V3 组件治理
        • 10.总结与决策
          • 10.1 黄金五法则
          • 10.2 选型决策树
      • 组件化方案的设计
      • 插件化与热加载方案
      • SDK设计与发布方案
      • 中台化能力沉淀方案
      • 配置中心设计方案
    • 数据与存储

    • 通信与协议

    • 稳定性与安全

    • 端侧专项性

    • 研发的效能

  • 专栏
  • 方案设计思想
  • 架构与组件
杨充
2025-11-14
目录

通用架构设计方案

# 01.通用架构设计方案

本篇定位:架构设计是所有方案的"地基"。本文不讲花哨的概念,而是回答三个问题——为什么必须做架构?业界主流方案怎么演进的?我该选哪种?

# 目录介绍

  • 01.一个真实的事故
    • 1.1 事故背景还原
    • 1.2 复盘根因定位
    • 1.3 反思架构价值
  • 02.要解决什么问题
    • 2.1 复杂度的失控
    • 2.2 协作的边界
    • 2.3 演进的成本
    • 2.4 三个核心矛盾
  • 03.业界主流方案
    • 3.1 三种架构溯源
    • 3.2 横向对比矩阵
    • 3.3 代码结构对比
  • 04.架构设计原则
    • 4.1 三条优先级法则
    • 4.2 SOLID 落地解读
    • 4.3 分层依赖原则
  • 05.MVC 方案落地
    • 5.1 核心结构图
    • 5.2 数据流时序
    • 5.3 适用与不适用
  • 06.MVP 方案落地
    • 6.1 核心结构图
    • 6.2 数据流时序
    • 6.3 适用与不适用
  • 07.MVVM 方案落地
    • 7.1 核心结构图
    • 7.2 数据流时序
    • 7.3 适用与不适用
  • 08.架构设计陷阱
    • 8.1 过度设计反例
    • 8.2 欠缺设计反例
    • 8.3 选型错配反例
  • 09.架构演进路线
    • 9.1 V1 单体启动
    • 9.2 V2 分层规范
    • 9.3 V3 组件治理
  • 10.总结与决策
    • 10.1 黄金五法则
    • 10.2 选型决策树

# 01.一个真实的事故

# 1.1 事故背景还原

某团队接手一个迭代了 3 年的 App 项目,单 Activity 文件 4200 行,登录逻辑、网络请求、UI 刷新、埋点全在一个 onCreate 里。某次产品提了一个看似简单的需求:"登录后给老用户弹一个优惠券"。结果上线第二天炸了——

  • 现象:30% 老用户进入 App 闪退
  • 影响:当天日活下跌 18%,丢单约 200 万
  • 修复:紧急回滚,3 天后重新发版

# 1.2 复盘根因定位

回头看代码,那段"加优惠券弹窗"的逻辑只有 12 行,但它依赖了 7 个全局变量、3 个静态单例和 1 个还没初始化的 Service。真正的根因不是这 12 行代码,而是过去 3 年没有人定义过"什么逻辑应该写在哪一层"。

flowchart TD
    A[需求: 登录后弹优惠券] --> B[开发: 在 Activity 加 12 行]
    B --> C{这 12 行依赖什么?}
    C --> D[7 个全局变量]
    C --> E[3 个静态单例]
    C --> F[未初始化的 Service]
    D --> G[NPE]
    E --> G
    F --> G
    G --> H[闪退]
    
    style A fill:#e3f2fd
    style H fill:#ffebee
1
2
3
4
5
6
7
8
9
10
11
12
13

# 1.3 反思架构价值

这次事故揭示了一个朴素但被忽视的事实:没有架构 ≠ 没有结构,而是结构由"最后一个写代码的人"随机决定。架构设计的本质,不是写更多代码,而是用一套规则约束未来所有迭代。

架构 = 一组可以让团队在 3 年后依然敢于改动它的约束。

带着这个事故的痛感,我们重新审视架构设计要解决的问题。

# 02.要解决什么问题

# 2.1 复杂度的失控

软件复杂度有两类:本质复杂度(业务本身就难,例如金融风控规则)和偶然复杂度(架构腐坏带来的难,例如全局变量随处可改)。架构能消除的只有偶然复杂度,但偶然复杂度一旦失控,会比本质复杂度大 5~10 倍。

graph TD
    A[总复杂度] --> B[本质复杂度<br/>业务本身的难度]
    A --> C[偶然复杂度<br/>架构腐坏的代价]
    
    B --> B1[领域规则复杂]
    B --> B2[一致性要求高]
    B --> B3[性能要求严]
    
    C --> C1[全局变量满天飞]
    C --> C2[循环依赖]
    C --> C3[职责越界]
    
    style B fill:#e8f5e8
    style C fill:#ffebee
1
2
3
4
5
6
7
8
9
10
11
12
13
14

架构的第一个使命:把偶然复杂度压到最低。

# 2.2 协作的边界

3 人团队靠口头约定就能高效协作,但 30 人团队没有架构约束就会迅速变成"代码管不住"。架构通过强制的边界(包/模块/组件)让"我不需要看你的代码也能和你协作"成为可能。

团队规模 没有架构 有架构
3 人 还行,靠默契 收益不大
10 人 开始打架 开始有价值
30 人 频繁回滚 必须
100 人 不可能交付 唯一选择

# 2.3 演进的成本

架构的真正回报不在第一次写完,而在第 N 次改它。一个好的架构,让你 3 年后改一行代码只需要看 3 个文件;一个坏的架构,让你 3 个月后改一行代码就得跑全量回归。

graph LR
    A[改动成本] --> B[坏架构: 改动成本 ∝ 代码总量]
    A --> C[好架构: 改动成本 ∝ 改动范围]
    
    style B fill:#ffebee
    style C fill:#e8f5e8
1
2
3
4
5
6

# 2.4 三个核心矛盾

把上面的问题抽象一下,架构设计实际是在解三组矛盾:

矛盾 一端 另一端 架构的作用
耦合 vs 复用 调用越直接越快 直接调用就耦合死了 通过抽象层让它们共存
简单 vs 扩展 越简单越好维护 太简单不够灵活 留出"可扩展点"
稳定 vs 演进 不动最稳 不动就过时 提供安全的演进路径

理解了"要解决什么问题",再看业界给出的方案就有的放矢了。

# 03.业界主流方案

# 3.1 三种架构溯源

为什么会出现 MVC? 1979 年 Trygve Reenskaug 在 Smalltalk 里提出 MVC,要解决的问题非常具体——让 GUI 程序的视图刷新和业务逻辑解耦。这是计算机历史上第一次把"视图"和"模型"在代码层面分开。

为什么会演化出 MVP? 90 年代桌面程序变得越来越复杂,MVC 的 View 还是会回头读 Model(双向耦合),导致单元测试几乎不可能写。MVP 的发明者(Mike Potel)做了一个看似很小但极关键的改动:让 View 变成"被动"的,所有逻辑收敛到 Presenter。这一改让 GUI 单元测试第一次成为可能。

为什么会出现 MVVM? 2005 年微软 WPF 团队遇到一个新问题:UI 越来越声明式(XAML),但 Presenter 里到处是 view.setText() 这种命令式代码。他们把"声明式 UI + 数据绑定"组合起来,让 ViewModel 完全不需要持有 View 的引用。这是响应式编程的雏形。

flowchart LR
    A[1979 MVC<br/>解决 GUI 与业务的解耦] --> B[1990s MVP<br/>解决 GUI 的可测试性]
    B --> C[2005 MVVM<br/>解决命令式 UI 的繁琐]
    C --> D[2015+ 现代架构<br/>组件化 + 响应式 + 单向数据流]
    
    style A fill:#e3f2fd
    style B fill:#e8f5e8
    style C fill:#fff3e0
    style D fill:#f3e5f5
1
2
3
4
5
6
7
8
9

关键洞察:每一次架构演进,都是为了解决前一代留下的具体痛点,不是为了"更先进"。这也是后面 §4 我们强调"合适优于先进"的根本原因。

# 3.2 横向对比矩阵

维度 MVC MVP MVVM
诞生年份 1979 1990s 2005
核心目标 视图与模型分离 视图被动化 + 可测试 数据绑定 + 声明式 UI
View 是否有逻辑 有部分 完全没有 只有声明式绑定
可测试性 弱(View-Model 耦合) 强(Presenter 可单测) 中(依赖绑定框架)
代码量 少 多(接口爆炸) 中
性能开销 低 低 中(绑定监听)
学习曲线 平 中 陡
团队适配 小团队 / 简单页面 中型团队 / 复杂逻辑 大型团队 / 重交互
典型场景 后台管理页 表单密集型 App 实时数据看板

# 3.3 代码结构对比

同样一个"用户登录"场景,三种架构的代码组织截然不同:

组件 MVC MVP MVVM
触发入口 Controller 接收点击 View 委托给 Presenter View 触发 Command
业务逻辑位置 Controller / Model Presenter ViewModel
UI 更新方式 Model 通知 View Presenter 调 View 接口 数据绑定自动更新
失败处理 View 自己判断 Presenter 调 showError() ViewModel 设置 errorState
单元测试 需启动 UI Mock View 接口即可 直接测 ViewModel

# 04.架构设计原则

# 4.1 三条优先级法则

回到那个事故团队的真实选择题——他们要把 4200 行的 Activity 改成什么?答案不是"最先进的架构",而是"当下最合适的"。这就引出了架构设计第一性的三条法则:

合适优于先进 > 演化优于一步到位 > 简单优于复杂

法则 反面案例 正确做法
合适优于先进 一个 5 人团队的内部工具上微服务 + K8s 单体 + Docker 就够
演化优于一步到位 一开始就规划 50 个组件的拆分 先拆 3 个核心组件,跑半年看效果
简单优于复杂 引入 6 个中间件解决一个问题 能用 if-else 解决的不上策略模式

举个例子:MVP 模式很流行,理论上能降低耦合。但事实是,引入 MVP 后代码极度膨胀,新增了大量 Contract 接口,可读性反而下降。用了 MVP 真的让维护成本降低了吗?这取决于你的项目规模——10 个页面以下的 App,MVC 完全够用。

# 4.2 SOLID 落地解读

SOLID 五原则讲烂了,但真正能用在架构里的只有两条最关键:

graph TB
    subgraph "SOLID 原则"
        S[单一职责 SRP<br/>一个类只有一个变化的理由]
        O[开闭原则 OCP<br/>对扩展开放, 对修改关闭]
        L[里氏替换 LSP<br/>子类可替换父类]
        I[接口隔离 ISP<br/>接口最小可用]
        D[依赖倒置 DIP<br/>依赖抽象不依赖具体]
    end
    
    S -.->|最关键| Core[架构两根支柱]
    D -.->|最关键| Core
    
    style S fill:#e3f2fd
    style D fill:#ffebee
    style Core fill:#fff3e0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • SRP(单一职责) 是分层和分模块的根本依据——每一层、每一个组件,都应该只为"一种变化"负责
  • DIP(依赖倒置) 是解耦的根本手段——上层依赖抽象接口,而不是依赖具体实现,这样替换实现就不影响上层

# 4.3 分层依赖原则

graph TB
    subgraph "经典四层架构"
        P[表现层 Presentation<br/>UI / 交互 / 展示]
        B[业务层 Business<br/>业务规则 / 工作流]
        S[服务层 Service<br/>数据访问 / 外部集成]
        D[数据层 Data<br/>持久化 / 数据源]
    end
    
    P --> B
    B --> S
    S --> D
    
    P -. ❌ 禁止跨层 .-> S
    P -. ❌ 禁止跨层 .-> D
    B -. ❌ 禁止反向 .-> P
    
    style P fill:#e3f2fd
    style B fill:#e8f5e8
    style S fill:#fff3e0
    style D fill:#f3e5f5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

三条铁律:

  1. 单向依赖:上层依赖下层,绝不允许反向
  2. 不跨层:表现层不能直接访问数据层(除非这个项目就不打算分层)
  3. 依赖接口:跨层调用走接口,方便替换底层实现

这三条铁律一旦写进 Code Review 检查清单,架构腐坏速度会下降一个数量级。

# 05.MVC 方案落地

# 5.1 核心结构图

graph TB
    M[Model<br/>数据 + 业务规则]
    V[View<br/>UI 展示 + 部分交互]
    C[Controller<br/>事件处理 + 协调]
    
    V -->|用户操作| C
    C -->|更新数据| M
    M -->|数据变化通知| V
    C -->|控制视图| V
    
    style M fill:#e3f2fd
    style V fill:#e8f5e8
    style C fill:#fff3e0
1
2
3
4
5
6
7
8
9
10
11
12
13

为什么 View 和 Model 还有箭头? 这是 MVC 的"原罪"——View 会订阅 Model 的变化(观察者模式),所以 View 知道 Model 长什么样。这就是后来 MVP 要"切断"的那条边。

# 5.2 数据流时序

sequenceDiagram
    participant User as 用户
    participant View as View
    participant Controller as Controller
    participant Model as Model
    
    User->>View: 点击按钮
    View->>Controller: 转发事件
    Controller->>Controller: 验证输入
    Controller->>Model: 调用业务方法
    Model->>Model: 处理业务逻辑
    Model->>View: 通知数据变化(观察者)
    View->>View: 自行刷新
    View->>User: 显示结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5.3 适用与不适用

场景 是否适用 原因
后台管理系统 ✅ 适用 页面简单,不需要复杂交互
简单 App(< 10 页) ✅ 适用 学习成本低,开发快
重交互 App ❌ 不适用 View-Model 耦合会阻碍迭代
强测试要求 ❌ 不适用 View 难以 Mock
多端共用业务逻辑 ❌ 不适用 业务嵌在 Controller,难复用

Android 的特殊性:很多人吐槽 Android 的 MVC 不"纯",根源在于 Activity 既是 Controller 又是 View 的容器。XML 是声明式的(View),但布局逻辑(动态显隐、颜色变化)只能写在 Activity 里,于是 Activity 就胀成了"超级类"。这也是 Android 圈最早全面拥抱 MVP 的原因。

# 06.MVP 方案落地

# 6.1 核心结构图

graph TB
    M[Model<br/>数据 + 业务]
    V[View<br/>纯 UI / 被动]
    P[Presenter<br/>表现逻辑]
    
    V <-->|接口契约| P
    P <-->|调用| M
    
    style M fill:#e3f2fd
    style V fill:#e8f5e8
    style P fill:#fff3e0
1
2
3
4
5
6
7
8
9
10
11

关键改动:MVP 把 View 和 Model 之间的箭头切断了,换成 View ↔ Presenter ↔ Model 的链式关系。View 不再知道 Model 长什么样,它只认 Presenter 给它的"展示数据"。这就让 View 可以被任意替换、Mock。

# 6.2 数据流时序

sequenceDiagram
    participant User as 用户
    participant View as View
    participant Presenter as Presenter
    participant Model as Model
    
    User->>View: 用户操作
    View->>Presenter: 委托处理
    Presenter->>View: showLoading()
    Presenter->>Model: 请求数据
    Model-->>Presenter: 返回结果
    Presenter->>View: hideLoading()
    
    alt 成功
        Presenter->>View: showData(data)
    else 失败
        Presenter->>View: showError(message)
    end
    
    View->>User: 更新界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

注意 Presenter 调用 View 都是通过接口(如 IUserView.showData()),所以单元测试时可以 Mock 一个 IUserView 实现,完全不需要启动 UI。

# 6.3 适用与不适用

场景 是否适用 原因
表单密集型 App ✅ 适用 表单校验逻辑收敛在 Presenter
强测试覆盖要求 ✅ 适用 View 可 Mock,单测好写
多平台共享业务 ✅ 适用 Presenter 抽出来跨端复用
简单展示页面 ❌ 过度设计 Contract 接口反而让代码膨胀
强依赖 UI 框架 ❌ 困难 比如 Compose / SwiftUI 这种声明式 UI 与 MVP 思路冲突

MVP 的副作用:每个页面都要写 Contract(IXxxView + IXxxPresenter),新增一个简单页面也要建 4-5 个类。我见过一个 50 页面的 App 用了 MVP 后代码量翻了 1.8 倍——这就是"过度设计"的代价。

# 07.MVVM 方案落地

# 7.1 核心结构图

graph TB
    M[Model<br/>数据 + 业务]
    V[View<br/>声明式模板]
    VM[ViewModel<br/>状态 + 命令]
    
    V <-->|数据绑定| VM
    VM -->|调用| M
    M -->|返回| VM
    
    style M fill:#e3f2fd
    style V fill:#e8f5e8
    style VM fill:#fff3e0
1
2
3
4
5
6
7
8
9
10
11
12

关键创新:ViewModel 完全不知道 View 的存在,它只管暴露状态(如 LiveData<UserInfo>)和命令(如 loginCommand)。View 通过"绑定语法"自动响应状态变化——这就是响应式编程的本质。

# 7.2 数据流时序

sequenceDiagram
    participant User as 用户
    participant View as View
    participant Binding as 绑定层
    participant VM as ViewModel
    participant Model as Model
    
    User->>View: 输入或点击
    View->>Binding: 双向绑定触发
    Binding->>VM: 更新状态 / 调用命令
    VM->>Model: 请求业务
    Model-->>VM: 返回结果
    VM->>VM: 更新可观察状态
    VM->>Binding: 通知变化
    Binding->>View: 自动刷新 UI
    View->>User: 显示结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 7.3 适用与不适用

场景 是否适用 原因
实时数据看板 ✅ 适用 数据变化频繁,绑定省去手动刷新
复杂表单(多字段联动) ✅ 适用 联动逻辑天然适合响应式
Compose / SwiftUI / Vue ✅ 必选 这些框架本身就是 MVVM 思路
简单静态页 ❌ 过度设计 不需要绑定,用 MVC 就行
性能敏感的列表 ⚠️ 谨慎 大量绑定监听会有开销

MVVM 的副作用:绑定让数据流变"隐式",调试时 BUG 链会断在绑定框架内部,新人很难上手。一个常见的踩坑是 ViewModel 持有 Context 导致内存泄漏——这就需要严格的 Lint 规则约束。

# 08.架构设计陷阱

知道哪些坑不能踩,比知道哪些方案好用更重要。

# 8.1 过度设计反例

反例:5 人团队的内部 OA 工具,团队负责人引入 Clean Architecture(4 层 + UseCase + Repository + DataSource),每个简单的"获取列表"接口要写 8 个类。

问题:6 个月后团队抱怨"加一个字段要改 8 个文件",开发效率比纯 MVC 慢 3 倍。

教训:架构复杂度必须与团队规模 + 业务复杂度匹配。Clean Architecture 是为 100 人级别团队 + 高频变更业务设计的,5 人内部工具用它就是用大炮打蚊子。

# 8.2 欠缺设计反例

反例:本文开头那个 4200 行 Activity 的事故,就是典型的"欠缺设计"——团队从来没有约定过任何分层规则。

问题:每个新开发都按自己习惯加代码,3 年后已经无法回溯任何变更链路。

教训:架构最低标准 = 团队对"代码该写在哪"达成共识,哪怕只有一张纸的规则。

# 8.3 选型错配反例

反例:一个图片浏览 App(核心是图片列表 + 详情页),团队全面铺 MVVM + DataBinding。

问题:图片列表性能下降 30%,因为每个 Cell 都注册了 4 个 LiveData 观察者,滑动时频繁触发绑定计算。

教训:性能敏感场景慎用 MVVM,列表 Cell 这种高频复用对象用普通 MVC + ViewHolder 反而更好。

mindmap
  root((三类常见陷阱))
    过度设计
      团队太小却用复杂架构
      过早抽象
      面向未来不存在的需求
    欠缺设计
      没有任何分层约定
      凭经验写代码
      技术债越滚越大
    选型错配
      性能敏感用重架构
      简单场景上 MVP
      声明式 UI 套 MVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 09.架构演进路线

真实的架构是"长出来"的,不是一次性设计完成的。下面以那个事故团队的真实演进为例。

# 9.1 V1 单体启动

团队规模:3 人 / 业务规模:5 个核心页面 / 架构选择:Activity + Service 直接调用

graph LR
    A[Activity] --> S[Service]
    A --> DB[SQLite]
    
    style A fill:#e3f2fd
1
2
3
4
5

为什么这样选:3 人团队、5 个页面,分层只会让简单事情变复杂。 何时该升级:页面数 > 15 或团队 > 8 人。

# 9.2 V2 分层规范

团队规模:10 人 / 业务规模:30 个页面 / 架构选择:MVP + Repository

graph TB
    V[View / Activity] --> P[Presenter]
    P --> R[Repository]
    R --> N[Network]
    R --> DB[Local DB]
    
    style V fill:#e3f2fd
    style P fill:#e8f5e8
    style R fill:#fff3e0
1
2
3
4
5
6
7
8
9

为什么这样选:10 人团队需要明确边界 + 强制单测,MVP 是性价比最高的选择。 带来的痛:Contract 接口爆炸,每个页面都要写 IView/IPresenter。 何时该升级:跨业务线复用需求强烈、团队 > 30 人。

# 9.3 V3 组件治理

团队规模:30+ 人 / 业务规模:100+ 页面 / 架构选择:MVVM + 组件化 + 单向数据流

graph TB
    subgraph "应用壳"
        Shell[Shell App]
    end
    
    subgraph "业务组件"
        Comp1[用户组件<br/>MVVM]
        Comp2[订单组件<br/>MVVM]
        Comp3[支付组件<br/>MVVM]
    end
    
    subgraph "通用层"
        Common[路由 / 网络 / 存储 / UI Kit]
    end
    
    Shell --> Comp1
    Shell --> Comp2
    Shell --> Comp3
    Comp1 --> Common
    Comp2 --> Common
    Comp3 --> Common
    
    style Shell fill:#ffebee
    style Common fill:#e8f5e8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

为什么这样选:组件化让大团队可以独立开发独立发布,MVVM 配合声明式 UI 大幅降低样板代码。 带来的痛:组件间通信成本上升,需要专门的路由方案(详见本卷 02 篇)。

⚠️ 关键提醒:不是每个团队都要走完 V1→V2→V3。走到自己业务能 Hold 住的那一站就停——很多创业公司终身停留在 V1,反而活得很好。

# 10.总结与决策

# 10.1 黄金五法则

  1. 业务优先 —— 架构服务于业务,不要为了技术而技术
  2. 渐进演进 —— 从简单开始,随着业务发展逐步演进
  3. 团队匹配 —— 架构复杂度要与团队能力匹配
  4. 持续改进 —— 架构不是一成不变的,要持续优化
  5. 文档先行 —— 好的架构需要好的文档支撑

# 10.2 选型决策树

flowchart TD
    Start([新项目启动]) --> Q1{页面数 < 15<br/>且团队 < 8 人?}
    Q1 -->|是| MVC[选 MVC<br/>开发快]
    Q1 -->|否| Q2{需要强测试覆盖<br/>或多端共享业务?}
    
    Q2 -->|是| Q3{UI 框架是声明式吗?<br/>Compose/SwiftUI/Vue}
    Q2 -->|否| MVC
    
    Q3 -->|是| MVVM[选 MVVM<br/>配合声明式 UI]
    Q3 -->|否| MVP[选 MVP<br/>测试友好]
    
    MVC --> Future{未来 1 年<br/>团队会扩大吗?}
    Future -->|会| Plan[预留分层接口<br/>方便升级到 MVP/MVVM]
    Future -->|不会| Stay[继续 MVC 即可]
    
    style MVC fill:#e3f2fd
    style MVP fill:#e8f5e8
    style MVVM fill:#fff3e0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

最后一句话:架构没有银弹,只有"在当前条件下最不坏的选择"。回到本文开头的事故,那个团队最终选了 V2(MVP + Repository)——不是因为 MVP 最先进,而是因为它的 10 人团队正好需要这种程度的约束。

好的架构 = 既能当下跑得动,又能 3 年后还敢改。

上次更新: 2026/06/07, 10:26:12
README
组件化方案的设计

← README 组件化方案的设计→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式