03 · 读懂与画好架构图(C4 模型)
上一章你学会了把系统想清楚。这一章解决下一个问题:怎么把脑子里那个系统,画成别人一眼就懂的图。 架构师一大半的工作不是设计,而是沟通——而图,是沟通的主力工具。
为什么架构师必须会画图
先纠正一个误解:画架构图,不是为了好看,也不是为了交差,而是为了一件最朴素的事——让别人和你看到的是同一个系统。
想象一下:你脑子里那个系统是清晰的,数据怎么流、谁依赖谁、边界在哪,你门儿清。但它锁在你脑子里。 你的产品经理不知道,后端同事各有各的理解,前端以为是另一回事。于是十个人,脑子里装着十个略有不同的系统——这是几乎所有项目混乱的根源。
架构图的作用,就是把你脑中那个系统**「倒」出来,摆到桌面上**,让所有人对着同一张图说话。它的价值,在于「对齐」:
没有图: 有图:
产品脑中 后端脑中 所有人
的系统 的系统 看着同一张
│ │ 图说话
▼ ▼ │
「我以为」 「我以为」 ▼
↘ ↙ 「我们说的
冲突、返工 是同一个东西」一句话:架构师的产出,不只是「想清楚」,还得是「让团队都清楚」。 想清楚了却讲不明白,在团队协作里,几乎等于没想清楚。会画图,是把个人的判断力,变成团队的共识——这是架构师的核心工作,不是附加技能。
而且,画图本身就是一种思考。 当你试图把脑中的系统画下来,常会发现「咦,这两个东西到底谁调谁?」「这里的边界其实我没想清楚」——图会逼你把含糊的地方想明白。 画不出来,往往就是还没想清楚的信号。
先看看「烂图」长什么样
在学怎么画好之前,先认认坏味道。你一定见过这种图:一堆五颜六色的方块,密密麻麻的箭头横七竖八,看半天不知道它想说什么。烂图比没有图更糟,因为它制造一种「我们沟通过了」的错觉,实际上每个人理解的还是不一样。
烂图通常犯这几个毛病:
┌─────────────────────────────────────────────────────────────┐
│ 烂图四宗罪 │
├─────────────────────────────────────────────────────────────┤
│ ① 没有边界 所有框平铺在一起,分不清哪些在一个进程/ │
│ 一台机器/一个信任域里。 │
│ │
│ ② 箭头没方向 线就是线,不知道是 A 调 B 还是 B 调 A, │
│ 也不知道线上跑的是什么(数据?调用?事件?) │
│ │
│ ③ 抽象层次混乱 一张图里,「整个支付系统」和「某个工具函数」 │
│ 画在同一层。大小不一的概念混在一起,看的人 │
│ 不知道该用什么颗粒度理解。 │
│ │
│ ④ 框太多 四十个方块挤在一页,信息量爆炸, │
│ 看的人直接放弃。 │
└─────────────────────────────────────────────────────────────┘第三条「抽象层次混乱」最隐蔽,也最致命,值得多说一句。它就像一张地图上,既画了「整个中国」,又画了「你家小区的某栋楼」——这两个东西的尺度差了十万八千里,放在一起,看的人完全不知道该站在多高的视角去理解。
好图的反面,正是治好这四宗罪:有清晰的边界、箭头标明方向和含义、一张图只用一个抽象层次、框的数量受控。 那么,有没有一套现成的方法,能帮我们系统地做到这些?有,它叫 C4 模型。
C4 模型:像用地图软件一样,分层级看系统
C4 模型的核心思想,和你用地图软件的体验一模一样。
你查路线时,不会用同一个缩放级别看完所有东西:**先看「中国地图」**知道城市在哪,**放大到「市区图」**看主干道,**再放大到「街道图」**看具体怎么走。每一层只显示那一层该有的信息,多一分则乱,少一分则空。
C4 把架构图也分成这样四个「缩放级别」,从最宏观到最微观:
缩放级别 C4 层次 画的是 给谁看
🌍 中国地图 → ① Context 上下文 整个系统 + 周边 所有人
(用户、外部系统) (含非技术)
│
▼ 放大,钻进「我们的系统」内部
🏙️ 市区图 → ② Container 容器 系统由哪几个 技术团队
「可独立运行的 (架构、运维、
大块」组成 技术负责人)
│
▼ 放大,钻进「某一个容器」内部
🛣️ 街道图 → ③ Component 组件 一个容器内部 开发这块的
由哪些模块组成 工程师
│
▼ 放大,钻进「某一个组件」内部
🏠 楼栋图 → ④ Code 代码 类、函数怎么组织 (基本不画,
交给 IDE)C4 这个名字,就来自这四层的英文首字母:Context、Container、Component、Code。
它最大的贡献,是直接根治了烂图的第三宗罪——「抽象层次混乱」。 因为它强制你:一张图,只待在一个缩放级别上。 你要么画整个系统的全貌,要么钻进某一块看它内部,绝不在一张图里混着画。
下面逐层看。重点掌握前两层(Context 和 Container),因为它们是日常用得最多、也最能体现架构判断的两层。
第①层:Context(系统上下文)—— 给所有人看
这是缩放到最远的一层。在这张图里,你的整个系统就是中间一个框(对,整个系统就一个框,内部细节一概不画),周围画的是:谁在用它(用户、各种角色)、它要和哪些外部系统打交道(支付网关、邮件服务、第三方登录……)。
它回答的是最高层的问题:「这个系统是干嘛的?它和外面的世界怎么交互?」
Context 图示例:一个「在线书店」
┌──────────┐ ┌──────────────┐
│ 顾客 │ │ 出版社系统 │
│ (用户) │ │ (外部,供货) │
└────┬─────┘ └──────▲───────┘
│ 浏览、下单 │ 同步库存
▼ │
┌─────────────────────────────────────────┴────┐
│ │
│ 在 线 书 店 系 统 │
│ (我们要做的东西,内部先不展开) │
│ │
└──────┬──────────────────────────┬─────────────┘
│ 发起支付 │ 发送订单邮件
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 支付网关 │ │ 邮件服务 │
│ (外部) │ │ (外部) │
└──────────────┘ └──────────────┘给谁看:所有人,包括产品经理、老板、客服——任何不写代码但需要理解「这系统在生态里扮演什么角色」的人。 这张图的妙处是,它一个技术名词都没有,非技术的人也能看懂,所以特别适合用来对齐「我们到底在做什么」。
第②层:Container(容器)—— 给技术团队看
把上一层中间那个「整个系统」的框放大、钻进去,你看到的就是 Container 层。
这里的「Container(容器)」不是指 Docker 那种容器(别被名字骗了)。在 C4 里,它指的是「一个可以独立运行、独立部署的大块」——比如一个前端应用、一个后端服务、一个数据库、一个缓存。判断标准很简单:它是不是一个能单独启动、单独部署的进程或存储? 是,它就是一个 Container。
这一层回答:「我们的系统,内部由哪几个能独立运行的大块组成?它们之间怎么通信?数据存在哪?」
Container 图示例:把上面那个「在线书店系统」放大
┌──────────┐
│ 顾客 │
└────┬─────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────┐
│ 在线书店系统(虚线 = 系统边界,框内都是「我们的」) ┊
┊ ┊
┊ ┌──────────────┐ 调用 API ┌───────────────┐ ┊
┊ │ Web 前端 │ ─────────────────▶ │ 后端服务 │ ┊
┊ │ (浏览器应用) │ ◀───────────────── │ (处理业务逻辑) │ ┊
┊ └──────────────┘ 返回 JSON └───┬───────┬───┘ ┊
┊ │ │ ┊
┊ 读写订单/商品 │ │ 缓存热门商品
┊ ▼ ▼ ┊
┊ ┌──────────┐ ┌────────┐ ┊
┊ │ 数据库 │ │ 缓存 │ ┊
┊ └──────────┘ └────────┘ ┊
└──────────────────────────────────────────────────────────┘给谁看:技术团队——架构师、技术负责人、运维。 因为这一层直接对应「部署」和「运维」:每个 Container 通常就是一个要部署、要监控、可能要扩容的单元。这也是架构判断最密集的一层——「拆成几个服务」「数据库怎么分」「要不要加缓存」,这些上一章讲的取舍,大多体现在这张图上。
💡 你或许注意到了:本仓库每个模板的「架构全景图」,基本就画在 Container 这个层次。 比如 AI 对话产品模板 的第 4 节那张图,里面的「接入层、编排层、推理服务、会话存储、向量检索」,每一个都是一个可独立运行的大块——这就是一张典型的 Container 图。学会读 Container 图,你就能读懂本仓库几乎所有的全景图。
第③层:Component(组件)—— 给开发这块的人看
再放大,钻进某一个 Container(比如上面的「后端服务」),看它内部由哪些模块构成——比如「订单模块」「商品模块」「用户认证模块」,以及它们怎么协作。
Component 图示例:把「后端服务」这一个容器放大
┌──────────────────────────────────────────────────┐
│ 后端服务(放大看它内部) │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 订单模块 │ ───▶ │ 库存模块 │ │
│ └─────┬──────┘ └─────┬──────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 支付对接 │ │ 数据访问层 │ ──▶ (到数据库) │
│ └────────────┘ └────────────┘ │
└────────────────────────────────────────────────────┘给谁看:要开发这个 Container 的工程师。 它帮一个团队内部就「这块代码怎么分模块、谁负责谁」达成一致。注意:只有当某个 Container 复杂到值得拆解时,才需要画它的 Component 图——简单的就别画了。
第④层:Code(代码)—— 通常不画
最后一层,是具体到类、函数怎么组织。
说实话,这一层基本不用手画。 因为代码本身、以及 IDE 自动生成的关系图,就是最准确、最实时的「Code 图」——你手画一张,代码一改它就过时了。所以 C4 模型自己都说:Code 层按需偶尔画一下即可,绝大多数时候跳过。
这本身就是个朴素的工程智慧:别去手工维护一份「代码一改就过时」的文档。 你的精力,该花在 Context 和 Container 这种「相对稳定、又最需要沟通」的层次上。
画图的基本元素:就三样
C4 看着花哨,但你手里真正的「画笔」其实只有三样东西。把这三样的含义钉死,你的图就不会乱。
┌─────────┐
│ 框 │ = 一个「有职责的东西」。
└─────────┘ 它得能回答「你是干嘛的」。一个系统、一个服务、
一个模块都行,但同一张图里,所有框的「尺度」要一致。
───▶ = 箭头 = 一段「关系」。
关键:① 一定有方向(谁主动找谁);
② 一定标含义(线上跑的是「调用」?「数据」?「事件」?)
没方向、没含义的线,是烂图的头号来源。
┌ ─ ─ ─ ─ ┐
┊ 边界 ┊ = 一条「分界线」。
└ ─ ─ ─ ─ ┘ 圈住「属于一起的东西」。它可能是:
• 部署边界(这些跑在同一台机器/集群里)
• 信任边界(框内是我们可信的,框外的输入都不可信)
• 系统边界(哪些是「我们的」,哪些是外部的)这三样里,新人最常忽略「边界」,而它恰恰最能体现架构思维。 一条边界,常常同时意味着「部署在哪」和「信任到哪」——比如上一章那个 AI 模板里反复强调的「任何重新进入系统的外部数据都不可信」,在图上,就是一条把「我们的系统」和「外部输入」分开的信任边界。边界画在哪,往往就是安全和部署决策的所在。
几条能立刻让你的图变好的原则
把这五条记下来,贴在显示器边上。它们直接对治前面说的「四宗罪」:
一张图,只用一个抽象层次。 这是 C4 的灵魂,也是最重要的一条。别在画整个系统的图里,突然塞进一个具体函数。要么都是「大块」,要么都是「模块」,不许混。(治第③宗罪)
箭头一定标方向和含义。 每根线都要能回答两个问题:「谁找谁?」「为了什么?」。在线旁写上「调用 API」「读写数据」「发送事件」之类的几个字。一根没标注的线,就是一个没想清楚的依赖。(治第②宗罪)
控制框的数量(7±2)。 人一眼能抓住的东西大约就 7 个上下。一张图超过 9 个框,就该考虑:是不是该往上抽一层、或者拆成两张图了。 信息要分层喂,不是一次性砸过去。(治第④宗罪)
先画 Context,再往下钻。 永远从最宏观的那张图开始,确认「系统和外界的关系」对齐了,再钻进去画 Container。自顶向下,别一上来就陷进某个组件的细节里——那样你很容易只见树木不见森林。
图是给人看的,不是给机器看的。 时刻问自己:「我的读者是谁?他需要这个信息吗?」给老板看的 Context 图里别出现技术黑话;给工程师看的 Container 图别为了简洁省掉关键的数据流。永远为你的读者画图。
ASCII 画图实用技巧
本仓库统一用纯文本(ASCII)画图——因为它在任何地方都能显示、能直接放进 Markdown 和代码注释、还能用文本 diff 看出改了什么,朴素但极其实用。 这里给你几个上手的小技巧:
① 框:用方块字符围起来
┌─────────┐ 横线 ─ 竖线 │ 四个角 ┌ ┐ └ ┘
│ 服务 A │ 交叉 ┼ 丁字 ┬ ┴ ├ ┤
└─────────┘
② 箭头:标清方向
──▶ ◀── ▲ ▼ 单向依赖
◀──▶ 或上下各一条 双向交互(如「请求/响应」)
③ 边界:用虚线和实线区分
┌─────────┐ 实线框:一个具体的东西
└─────────┘
┌ ─ ─ ─ ─ ┐ 虚线框:一条边界(系统/信任/部署)
┊ ┊
└ ─ ─ ─ ─ ┘
④ 在箭头旁边加文字,说明这根线在干嘛(最重要!)
A ───调用 API───▶ B
A ◀──返回结果──── B几个小提醒:
- 先在草稿纸或白板上画,想清楚布局再誊成 ASCII。 ASCII 改起来不如随手画的方便,所以「先想后画」更重要。
- 善用对齐和留白。 把同一层的框横向对齐、纵向拉开,图会清爽很多。挤在一起的 ASCII 图,可读性会断崖式下降。
- 不必追求像素级完美。 ASCII 图的目标是「讲清结构和关系」,不是当艺术品。能让人看懂数据怎么流、谁依赖谁,就成功了。
你不用从头死记这些字符。最好的学法,是直接去本仓库的模板里「抄」——看 AI 对话产品模板 的全景图怎么用框和箭头,照着模仿,几次就熟了。
动手练一练
光看不练,学不会画图。给你一个具体的练习,强烈建议真的动手做一遍:
练习:
- 打开
../templates/,挑任意一个你感兴趣的系统(比如电商、社交信息流、实时通讯……)。- 先别看它的全景图! 只读它开头的「一句话定位」和「核心需求」,然后自己动手,画一张它的 Context 图:中间一个框是这个系统,周围是用户和它要打交道的外部系统。用纸笔或 ASCII 都行。
- 画的时候,刻意应用本章的原则:框是不是只用了一个抽象层次?箭头有没有标方向和含义?有没有圈出系统边界?
- 画完,再去对照模板里的架构全景图(那是一张 Container 图)。
对照时,重点体会两件事:
- 你的 Context 图(最远的视角)和它的 Container 图(钻进去一层),是不是正好差了一个「缩放级别」? ——感受 C4 分层的威力:你画的「整个系统一个框」,放大进去,就是模板里那一堆相互协作的大块。
- 模板全景图里,有没有你没想到的「外部系统」或「内部大块」? 它为什么需要那个东西?——这一问,又回到了第 02 章的「为什么」。画图不只是画图,它逼着你重新理解这个系统的每一个决策。
📌 真实案例:C4 模型不是我们发明的
本章用的 C4 模型,来自 Simon Brown 提出的同名方法论,如今是业界画架构图的事实标准之一。
- 📎 官方:c4model.com —— 有更完整的四层定义、记号约定和大量真实示例。
- 本仓库 21 个模板的全景图,基本都画在 C4 的 Container 层——学会读它,就能读懂仓库里几乎所有系统。
本章小结
- 画图的本质是「对齐」,让团队所有人看到同一个系统。架构师一大半的工作是沟通,图是沟通的主力工具;想清楚却讲不明白,约等于没想清楚。而且画图本身会逼你把含糊处想透。
- 烂图四宗罪:没边界、箭头没方向、抽象层次混乱、框太多。 其中「抽象层次混乱」最致命——就像一张地图既画整个国家又画你家楼栋。
- C4 模型像地图软件的缩放级别,分四层:Context(整个系统+周边,给所有人)→ Container(可独立运行的大块,给技术团队)→ Component(容器内的模块,给开发者)→ Code(基本不画)。它的灵魂是:一张图只待在一个缩放级别。
- 本仓库的全景图,基本都是 Container 层的图。 学会读它,就能读懂仓库里几乎所有系统。
- 画图的画笔只有三样:框(有职责的东西)、箭头(标方向和含义的关系)、边界(部署/信任/系统的分界)。 新人最易忽略边界,而它最能体现架构思维。
- 五条原则:一张图一个抽象层次、箭头标方向和含义、控制框数(7±2)、先 Context 再下钻、永远为你的读者画图。
到这里,第一段「建立思维」就走完了:你知道了为什么架构判断越来越值钱(01),有了一套想清楚系统的框架(02),也学会了把想法画出来、讲明白(03)。
但你可能会问:每次都从零开始想吗?前人遇到的那些经典难题,有没有现成的「招式」可以套用?
有。接下来的第二段「掌握工具箱」,就是来给你发牌的——架构师手里那些反复被验证过的「模式」,每一个都是对某一类问题的标准答案。
👉 继续阅读:04 · 十大核心架构模式