03 · 读懂与画好架构图:C4 模型
上一章你学会了把系统想清楚。这一章解决下一个问题:怎么把脑子里那个系统,画成别人一眼就懂的图。 架构师一大半的工作不是设计,而是沟通——而图,是沟通的主力工具。
为什么架构师必须会画图
先纠正一个误解:画架构图,不是为了好看,也不是为了交差,而是为了一件最朴素的事——让别人和你看到的是同一个系统。
想象一下:你脑子里那个系统是清晰的,数据怎么流、谁依赖谁、边界在哪,你门儿清。但它锁在你脑子里。 你的产品经理不知道,后端同事各有各的理解,前端以为是另一回事。于是十个人,脑子里装着十个略有不同的系统——这是几乎所有项目混乱的根源。
架构图的作用,就是把你脑中那个系统倒出来,摆到桌面上,让所有人对着同一张图说话。它的价值,在于对齐:
没有图: 有图:
产品脑中 后端脑中 所有人
的系统 的系统 看着同一张
│ │ 图说话
▼ ▼ │
我以为 我以为 ▼
↘ ↙ 我们说的
冲突、返工 是同一个东西一句话:架构师的产出,不只是想清楚,还得让团队都清楚。 想清楚了却讲不明白,在团队协作里,几乎等于没想清楚。会画图,是把个人的判断力,变成团队的共识——这是架构师的核心工作,不是附加技能。
而且,画图本身就是一种思考。 当你试图把脑中的系统画下来,常会发现「咦,这两个东西到底谁调谁?」「这里的边界其实我没想清楚」——图会逼你把含糊的地方想明白。 画不出来,往往就是还没想清楚的信号。
先看看烂图长什么样
在学怎么画好之前,先认认坏味道。你一定见过这种图:一堆五颜六色的方块,密密麻麻的箭头横七竖八,看半天不知道它想说什么。烂图比没有图更糟,因为它制造一种我们沟通过了的错觉,实际上每个人理解的还是不一样。
烂图通常犯这几个毛病:
┌─────────────────────────────────────────────────────────────┐
│ 烂图四宗罪 │
├─────────────────────────────────────────────────────────────┤
│ ① 没有边界 所有框平铺在一起,分不清哪些在一个进程/ │
│ 一台机器/一个信任域里。 │
│ │
│ ② 箭头没方向 线就是线,不知道是 A 调 B 还是 B 调 A, │
│ 也不知道线上跑的是什么:数据?调用?事件? │
│ │
│ ③ 抽象层次混乱 一张图里,整个支付系统和某个工具函数 │
│ 画在同一层。大小不一的概念混在一起,看的人 │
│ 不知道该用什么颗粒度理解。 │
│ │
│ ④ 框太多 四十个方块挤在一页,信息量爆炸, │
│ 看的人直接放弃。 │
└─────────────────────────────────────────────────────────────┘第三条抽象层次混乱最隐蔽,也最致命,值得多说一句。它就像一张地图上,既画了整个中国,又画了你家小区的某栋楼——这两个东西的尺度差了十万八千里,放在一起,看的人完全不知道该站在多高的视角去理解。
好图的反面,正是治好这四宗罪:有清晰的边界、箭头标明方向和含义、一张图只用一个抽象层次、框的数量受控。 那么,有没有一套现成的方法,能帮我们系统地做到这些?有,它叫 C4 模型。
C4 模型:像用地图软件一样,分层级看系统
C4 模型的核心思想,和你用地图软件的体验一模一样。
你查路线时,不会用同一个缩放级别看完所有东西:先看中国地图知道城市在哪,放大到市区图看主干道,再放大到街道图看具体怎么走。每一层只显示那一层该有的信息,多一分则乱,少一分则空。
C4 把架构图也分成这样四个缩放级别,从最宏观到最微观:
缩放级别 C4 层次 画的是 给谁看
🌍 中国地图 → ① Context 上下文 整个系统 + 周边 所有人
用户、外部系统 含非技术
│
▼ 放大,钻进我们的系统内部
🏙️ 市区图 → ② Container 容器 系统由哪几个 技术团队
可独立运行的 架构、运维、
大块组成 技术负责人
│
▼ 放大,钻进某一个容器内部
🛣️ 街道图 → ③ Component 组件 一个容器内部 开发这块的
由哪些模块组成 工程师
│
▼ 放大,钻进某一个组件内部
🏠 楼栋图 → ④ Code 代码 类、函数怎么组织 基本不画,
交给 IDEC4 这个名字,就来自这四层的英文首字母: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 —— 有更完整的四层定义、记号约定和大量真实示例。
- 本仓库 26 个模板的全景图,基本都画在 C4 的 Container 层——学会读它,就能读懂仓库里几乎所有系统。
🎯 随堂检验
- A画一张超大图,从用户一直画到每个类和方法,一次讲全
- B按 Context → Container → Component 分层出图,每张图只待在一个抽象级别
- C只画 Container 层,因为技术团队才需要看图
本章小结
- 画图的本质是对齐,让团队所有人看到同一个系统。架构师一大半的工作是沟通,图是沟通的主力工具;想清楚却讲不明白,约等于没想清楚。而且画图本身会逼你把含糊处想透。
- 烂图四宗罪:没边界、箭头没方向、抽象层次混乱、框太多。 其中抽象层次混乱最致命——就像一张地图既画整个国家又画你家楼栋。
- C4 模型像地图软件的缩放级别,分四层:Context,整个系统+周边,给所有人;Container,可独立运行的大块,给技术团队;Component,容器内的模块,给开发者;Code,基本不画。它的灵魂是:一张图只待在一个缩放级别。
- 本仓库的全景图,基本都是 Container 层的图。 学会读它,就能读懂仓库里几乎所有系统。
- 画图的画笔只有三样:框、箭头、边界。 框代表有职责的东西,箭头代表标方向和含义的关系,边界代表部署/信任/系统的分界。新人最易忽略边界,而它最能体现架构思维。
- 五条原则:一张图一个抽象层次、箭头标方向和含义、控制框数 7±2、先 Context 再下钻、永远为你的读者画图。
到这里,第一段建立思维就走完了:你知道了为什么架构判断越来越值钱,见 01;有了一套想清楚系统的框架,见 02;也学会了把想法画出来、讲明白,见 03。
但你可能会问:每次都从零开始想吗?前人遇到的那些经典难题,有没有现成的招式可以套用?
有。接下来的第二段掌握工具箱,就是来给你发牌的——架构师手里那些反复被验证过的模式,每一个都是对某一类问题的标准答案。
👉 继续阅读:04 · 十大核心架构模式
💬 评论