用C4模型重新理解架构图:四层抽象与实战规范
为什么你的架构图越画越没人看三个能解释的坏习惯先说个我观察很久的现象很多团队的文档库里躺着几十张架构图但真到新同事入职、方案评审、排查线上故障的时候没人愿意打开它们。不是大家懒是那些图“没法看”——要么一张图把所有东西都塞进去密密麻麻全是框要么只画了一堆模块方块谁和谁怎么通信完全没交代要么画完当天就没再更新过三个月后图里的服务已经删了一半。我早年也干过这种事。后来接触了 C4 模型才意识到问题不在“画图技巧”而在“画图纪律”。C4 模型不是什么高深理论它是一套把软件架构图按抽象层级拆成四层的组织方式Context上下文、Container容器、Component组件、Code代码。它的目标特别朴素——让你画的每一张软件架构图都正好对上某一种读者的需求不多不少。这篇文章我会把这四层拆开讲清楚结合我自己项目中画图踩过的坑讲一讲什么时候该画哪一层、用什么工具画、怎么让图不变成一次性消耗品。无论你是刚入门想搞懂架构图怎么画还是带团队想统一绘图规范这篇都值得看完。1. 越画越没人看的三个坏习惯我先替你踩过了1.1 一张图画完所有层级结果谁都不满意最典型的错误一张图里既有系统边界、又有应用服务器、又有数据库表、还画了几条 API 调用链甚至有人把微服务内部的类名都标上去了。表面上看信息量巨大实际上等于没有信息。你想象一下给业务方看这张图她只想确认“用户下单的数据会不会经过外部支付网关”结果眼前全是技术名词给新来的后端同事看他想知道“会员服务到底依赖哪些内部服务”却被顶层那些不相关的模块干扰给运维看他关心的是部署边界和端口依赖图上根本没有。一张图覆盖全部粒度的后果就是每个人都要费力地从噪音里找自己关心的那部分信息。人都是懒的找过一次不想找第二次于是这张图就被遗弃了。C4 模型的第一条纪律就是禁止跨层级混画系统上下文图只画边界容器图只画进程级部署单元组件图只画单个容器内部的逻辑分工。1.2 只画静态方块不画关系和协议另一个让架构图“失去灵魂”的毛病是方框画了一大片方块之间的箭头一条都没有。几个模块整整齐齐排在那里看起来像公司组织架构图完全看不出数据怎么流动、依赖方向是什么、用的是 HTTP 同步调用还是消息异步解耦。我见过不少“高颜值”架构图配色精致、圆角统一但箭头的含义完全没定义。两个框之间画一条线到底是调用、依赖、数据库连接还是消息订阅箭头方向代表数据流还是控制流另一方框有没有主动回调这些东西不交代清楚架构图上静态单元越多误导性越强。好的架构图信息和边的意义比节点的美观重要得多。每条边至少要能回答三个问题这条关系是什么类型顺着哪个方向底层用的什么协议或机制连这三点都没有的图宁可不画。1.3 面向“所有人”等于面向“没有人”我早期画图有个心态希望一张图能同时满足老板、产品、研发、运维的需求这样我可以少画几张。结果恰恰相反——为了满足所有人我不得不把所有细节都画上去然后每个人看到的都是自己不关心的部分。C4 模型真正改变我的是这个思路先想清楚这张图给谁看、为了解决什么问题再决定画到哪一层、保留哪些信息。给老板汇报系统现状系统上下文图就够了技术方案评审容器图是主力后端团队内部梳理代码结构才需要下沉到组件图。所以后面我会专门用一节讲“读者与层级的对应关系”这是 C4 模型最好用也最容易被忽略的部分。2. C4 模型的骨与肉四个抽象层级怎么一步步降维C4 模型最早是 Simon Brown 在 2011 年前后提出的。这位老兄常年做软件架构咨询他见过太多白板上聊得明明白白、一落成文档就全变形的架构图所以提了一套“用地图缩放的思路画架构图”的方法。名字本身很好记C4 Context Container Component Code。四个层级的抽象程度逐层降低就像地图 App 的缩放从国家看到城市从城市看到街道从街道看到具体建筑物再放大看建筑物里的房间。要特别注意 C4 不是四种“风格”而是四个“缩放档位”。你不可能让一张图同时处于两个档位只能选择一个合适的档位先放大或缩小。这也是整个模型的核心纪律。2.1 Level 1系统上下文System Context——整个系统的全景快照系统上下文图是整个 C4 模型里离得最远、抽象程度最高的一帧。它把你要建设的整个软件系统画成一个黑色大方框方框外面画的是使用系统的人角色、系统需要对接的外部系统、以及它们之间的交互方向。这层图需要保留的信息极少系统本身叫什么名字、边界在哪里谁在用这个系统不是用户明细而是角色比如“普通用户”“运营人员”系统依赖了哪些外部系统比如支付网关、短信服务、第三方登录箭头方向代表的数据流或控制流一个网上商城项目的系统上下文图可能只有 5 个元素顾客角色、运营人员角色、网上商城系统大框、支付网关外部系统、物流查询服务外部系统。箭头分别是顾客下单、运营管理、商城调用支付、商城查询物流。别小看这层图。它是所有新成员入职时的第一份地图也是跨部门对齐需求时的通用语言更是一切更细粒度图纸的起点。画这层图通常只需要 30 分钟花不了多少成本。但很多团队恰恰跳过了这一层一上来就画容器图或组件图结果大家连系统边界和外部的依赖关系都没对齐后面所有的讨论都是建立在空中楼阁上。2.2 Level 2容器图Container——进程级部署单元的拆解放大一档系统上下文里的大方框被展开成若干个“容器”。这里要立刻澄清一个几乎所有人都会踩的坑C4 里的 Container 跟 Docker 容器没有关系。在 C4 模型里“容器”指的是任何一个需要独立运行或独立部署的进程/单元。包括但不限于Web 应用浏览器里跑的 SPA、后端 API 服务、移动 App 客户端、关系型数据库、缓存中间件、消息队列、批处理任务、命令行脚本。判断标准就一条它是不是一个可以单独启动、单独部署、单独运维的东西是那就画成一个容器。容器图要表达的信息主要有三类系统内部包含哪些容器容器之间如何连接每个容器的技术选型编程语言、框架、中间件版本容器间的通信机制HTTP/REST、gRPC、WebSocket、消息队列、直接数据库访问等回到网上商城项目容器图可能是这样浏览器中的单页应用Vue/React→ 调用 API 服务Java Spring Boot→ 访问 MySQL 数据库、读写 Redis 缓存、向 Kafka 发订单事件后台管理系统也是浏览器里的另一个应用走同一个 API 服务。这一层是 C4 模型四层里最常用、最核心的一层。它是开发团队分工的参照系是运维排查故障时的拓扑图也是架构评审时讨论技术选型是否合理的主战场。一个系统的容器数量通常在 4~12 个比较健康一旦超过 15 个图就会开始失去可读性——这说明系统已经复杂到需要按业务域进一步拆分绘制了。2.3 Level 3组件图Component——容器内部的职责分区再放大一档我们选中某个容器把它展开成若干个组件。组件不是一个客观存在的东西它取决于你怎么组织代码。常见的组件划分方式是按业务模块、按技术分层、按领域功能。比如“API 服务”这个容器展开后可能是认证入口、商品查询控制器、订单服务、支付客户端、用户数据访问、消息生产者。每个组件是一块独立的职责区域组件之间依旧要有明确的调用关系。画组件图要注意一个数量控制原则一个容器内部建议画的组件不要超过 15 个。超过了说明这个容器已经大得搞不定可能需要拆分成多个容器或者你画得太细了把类级别的内容也混进来了。组件图的主要读者是负责这个容器的开发团队。它是团队内部做模块设计、划分 Git 仓库、安排开发任务时的参考依据。对外部团队来说这层图通常过于细节他们并不需要关心你 API 服务内部有哪些组件。2.4 Level 4代码Code——可以画但通常没必要最后一层是代码层就是把某个组件再放大画出它的类图、接口和关键实现关系。这一层的信息密度最高抽象程度最低本质上是把源码结构可视化了。我的观点很明确这一层不要手动画也不要在架构文档里作为重点维护。原因有两点第一代码层变化太快手动维护的成本极高。一个类改名、一个方法迁移图就过时了。维护一张过时的细粒度类图比没有类图更糟糕因为它会误导后来的人。第二现代 IDE 已经能根据源码实时生成类图和调用关系比如 IntelliJ IDEA 的 Diagram、VS Code 里的依赖可视化插件。工具能做的事情不该让文档工程师手工去做。代码层真正值得画的只有两类场景一是某些复杂组件的内部设计确实关键需要画一张静态图来帮助核心成员理解二是要做架构合规检查比如验证某个模块是否违反了依赖方向规则。这类场景下代码层作为“审计视角”存在而不是作为日常阅读文档。所以我的建议是四层并不需要全画大部分项目画到 Level 2 或局部 Level 3 就够了。Level 4 留给 IDE 和自动化工具手动画它属于典型的“用战术上的勤奋掩盖战略上的懒惰”。3. 选择哪一层先看看对面坐着的是谁3.1 受众与抽象层级映射关系我在前面反复说“图是给人看的”那到底给什么人看什么层级这里给一张我平时用于对齐团队认知的对应关系表基本可以解决 80% 的纠结读者角色推荐的抽象层级他们在图里想找什么业务方 / 产品经理Level 1必要时加部分 Level 2系统边界、外部依赖、用户交互路径架构评审委员会Level 2 局部 Level 3技术选型、容器拆分、通信协议后端开发团队Level 2 Level 3容器依赖关系、模块职责、调用方向新入职同事Level 1 → Level 2 → Level 3 逐层下钻从全局到局部的地图感运维 / SRELevel 2重点看部署和依赖部署单元、端口、数据库依赖、外部系统外部审计 / 合规Level 1 Level 3 关键组件数据流向、接口边界、风险控制点这张表不是死规矩但非常实用。每当你准备画图之前先问自己一句“坐在对面的是谁”然后对照表格决定画到哪一层。你会发现很多争论其实是层级没对齐引起的——一个人在白板前讲容器另一个人问的却是组件鸡同鸭讲。3.2 一个常见的误区每张图都要画到 Level 3我见过有团队把“全面”当目标要求所有业务域都输出完整的 Level 3 组件图结果画了三个月还画不完项目迭代早就跑到图前面去了。最后这些图被扔进 wiki 里吃灰画图的人也被拖垮了。画图的目的是以最小成本完成一次有效沟通不是交一份包罗万象的文档。跨团队有歧义时先画 Level 2 容器图把边界和依赖对齐团队内部要细化一个模块的职责时再挑关键容器画 Level 3 组件图。没必要所有容器都在同一时间画到组件级节奏应当是“按需下钻”。有一种检查方式我很推荐画完一张图找一个对项目不熟但有一定技术背景的同事给他 3 分钟。如果他能在看完图之后复述出主要模块、关键路径和依赖关系这张图就合格了如果他看得一头雾水说明层级不对或者信息超载了。3.3 根据场景切图C4 不只是一个文档而是一套文档体系C4 模型描述的是静态结构但软件架构的沟通不仅仅是静态的。时序、状态、部署、安全威胁这些维度也需要表达。我的经验是C4 负责“地图感”其他图负责“动态视角”。比如你要说明一次下单请求从浏览器到数据库的完整链路可以用一张时序图或活动图配合 C4 容器来画你要说明服务如何部署到多个可用区可以画部署图把它作为 Level 2 的补充视图你要说明某个模块的异常处理策略可以画状态图。C4 模型从来不是要取代所有其他架构图而是给架构图提供一套统一的“坐标体系”。在这套坐标体系下任何一张补充图都可以准确锚定到系统结构中的某一个位置——读者看到补充图的时候知道自己当前处在整个系统的哪一层、哪一块。这才是 C4 模型作为“导航术”的真正价值。4. 工具选型从免费画图到“文档即代码”C4 模型对工具没有硬性要求白板、PPT、绘图软件都能画。但工具会极大影响图的维护成本和协作效率。下面这几个方向我都实际用过按维护成本从低到高排个序。4.1 在线绘图型draw.iodiagrams.net这是目前最省事的方案。draw.io 内置了 C4 模型素材库能直接拖出“System Context”“Container”这类预制图形支持导出 SVG、PNG、PDF也能嵌入 Git 仓库或 wiki。它在线版可以直接打开浏览器用也支持本地客户端零安装门槛。优点是上手快图形美观度可以做得很好缺点是没有真正的版本控制语义——多人同时编辑容易互相覆盖图里改了什么也没有历史记录可查。所以我的建议是对于轻量级、一次性沟通的架构草图draw.io 完全够用但如果你要维护一份“长期有效、团队共同演进”的架构文档那它和 Git 的配合会别扭。4.2 文本优先方案Structurizr DSL如果团队对“文档即代码”有共识我非常推荐 Structurizr。它的核心思路是用一段 DSL领域特定语言描述系统模型然后由工具自动渲染出所有层级的架构图。举个例子一个极其简化的网上商城workspace { model { user person 普通用户 system softwareSystem 网上商城 { container Web前端 { component 商品浏览 component 购物车 } container API服务 container MySQL数据库 } user - system 使用商城 system.container API服务 - system.container MySQL数据库 读写 JDBC } views { systemContext system { include * autolayout } container system { include * autolayout } component API服务 { include * autolayout } } }这段文本有三个巨大优势进 Git 仓库天然支持版本控制和多人协作改图像改代码一样有 diff 可 Review模型和视图分离你定义一次模型可以自动生成上下文图、容器图、组件图等多个视图可以由 CI 自动构建每次主干变更都重新渲染产物部署到内部文档站Structurizr 的 DSL 有学习成本一开始会有点别扭但一旦团队里两三个人熟练了它的维护效率远超任何手动画图工具。我自己现在的项目就把这套 DSL 当成架构文档的“源码”图只是它的编译产物。4.3 文本绘图补充PlantUML 与手绘风工具PlantUML 也提供 C4 相关库画出来风格类似。它对一部分人来说比 Structurizr 更亲切因为已经习惯了用 PlantUML 画时序图、用例图顺带把架构图也画了。缺点是不像 Structurizr 那样有明确的“模型层”概念它更接近“用文本描述图形”。适合画单张图不适合做整套架构文档体系。如果追求的是白板感Excalidraw 这类手绘风工具很适合快速交流和截进 Slite、Notion 之类的笔记里。但它维护能力更弱基本只适合当快消品。三类工具不一定互斥我见到的常见组合是日常快速沟通用 Excalidraw 或白板正式架构文档用 Structurizr 或 PlantUML对外汇报用 draw.io 出美化过的图。关键是团队内部约定好“哪个是权威版本”别让同一张架构图在三个工具里各有一份。4.4 在线编辑和团队协作工具的取舍“c4 软件架构图在线编辑”是搜索比较热的关键词确实有不少团队想找免安装的在线协作方式。Structurizr 有云端版本可以直接在浏览器里用 DSL 建模并输出图draw.io 在线版则支持多人协作编辑。还有一些建模类工具支持 C4 风格界面更专业但通常要付费且上手复杂。选工具我只看三点第一是否支持导出 SVG第二是否支持版本化存储第三多人协作时的冲突策略。导出 SVG 保证了图能高质量嵌进文档和代码仓库版本化存储决定了图是“活文档”还是“死截图”冲突策略决定了团队能不能同时改一张图。按这三条标准筛大部分工具谁优谁劣立刻清楚了。5. 实战中的坑从踩坑到形成自己的绘图规范5.1 坑一把 Container 当成了 Docker 容器这个坑我在前面提过一次但必须重复——因为它几乎每个团队都会踩。我遇到过有架构师在评审会上一脸严肃地说“我们的容器图得把 Docker、K8s 画进去”结果整个讨论方向跑偏到基础设施上了。时刻记住C4 的 Container 是部署单元的抽象不关心你怎么部署它。同一个 Java 服务既可以部署在虚拟机里也可以打成 Docker 镜像跑在 K8s 上这并不影响它在容器图中的身份——它始终是同一个容器。如果你要表达部署架构请单独画部署图不要把它和 C4 容器混合在一起。否则你既画不好 C4也画不好部署图。5.2 坑二只画模块不画交互关系还有一种图方块画得整整齐齐但所有方块之间没有任何箭头。看起来像组织的架构图对软件系统没有任何解释力。我在团队规范里定了一条硬性规则每一条边都必须有名字说明是什么关系必须标明方向说明是请求方向还是返回方向尽量标明协议或机制。比如“API 服务 → MySQL 数据库读写数据JDBC”就比“API 服务 —— MySQL 数据库”强一百倍。这条规则会逼着画图的人去理清系统里真正发生的事情而不是画一个静态的组织结构图。5.3 坑三粒度没有对齐图悬在半空有些人画图没有层级意识一张图里既画了容器又混着组件甚至有数据库表级别的元素。这种图最让人抓狂看起来好像很全但没人知道该怎么读它。我管这个叫“悬空图”。防止它的办法就是前面反复强调的落笔前先定层级画完自检一遍如果图里有跨层级的元素立刻拆图。容器图只留容器和它们之间的连接组件图只在单个容器的边界内展开。这样做会让每张图的信息密度更均匀读图的人不需要自己“脑补缩放比例”。5.4 坑四过度追求视觉颜值忽视信息准确性很多团队画架构图把大量精力花在配色、圆角、阴影、图标上图确实好看了但图上传达的信息点反而被掩盖了。我不是反对美化而是建议把美观度放在信息准确性之后。先确保每一个框、每一条边都准确表达了你想要的架构决策然后再谈样式。此外样式应当有统一的语义。我常用的约定是外部系统用灰色框核心系统用品牌色角色用圆形容器用圆角矩形组件用直角矩形。这些约定一旦建立整文档体系会有一致性。值得注意样式约定必须写进团队规范里否则三个人画出三种配色最后还是要靠猜。5.5 坑五图画完就“死”了架构图最大的敌人不是画错而是过期。很多项目第一版架构图画得漂漂亮亮之后迭代了半年没人去更新它。半年后新同学一看图发现和线上代码差了一大截于是再也不信架构文档了。要把图从“一次性交付物”变成“活文档”最靠谱的方式是走文档即代码路线。把 Structurizr DSL 或 PlantUML 源文件放代码仓库每次架构变更一起提交CI 自动生成图片并部署到内部文档站。这样图就和代码同寿命永远不会因为没人手动更新而腐烂。如果团队实在没有精力上这套流水线至少约定凡是在图里描述的模块发生架构变更时修改代码的同一分支必须同时修改图。把图当作代码的一部分来评审而不是代码评审之外的另一件事。6. 把 C4 变成团队共识而不是某一个人的任务最后讲一点组织层面的事。我见过很多团队推行架构文档最终失败的共同原因都一样架构图成了架构师一个人的业余爱好其他人不参与、不买单。架构师辛辛苦苦画了全套 C4 图团队成员礼貌地看一眼然后继续按自己的习惯画局部草图。要避免这个结局最有效的方法是把画图变成评审会的“副产品”而不是事后的“作业”。比如技术设计评审时要求方案人在白板上先用 C4 框架勾出涉及的系统上下文和容器变化代码评审时如果这次改动引入了新的依赖或模块就要在同一个 MR 里更新对应的组件图。时间一长画图就不再是额外负担而是表达架构方案本身的自然方式。另外架构决策记录ADR和 C4 图是一对好搭档。ADR 记“为什么做这个决策”C4 图记“决策之后系统长什么样”。两者配合既能看到结构又能回溯理由。不用一次写很多每次一个关键决策配一张局部图积累一年团队的架构资产就会非常可观。在实际项目里我养成了一个挺简单的习惯把 C4 源文件放在代码仓库的docs/architecture/目录下任何一次涉及模块划分、容器依赖、通信协议变化的迭代都先改图再进迭代。一开始会觉得多花十几分钟但几个月后当你需要跟新同事讲系统全貌或者要搞清楚线上某个服务的依赖关系时你会非常感激当初留了这套东西。架构图的本质不是“画”而是“导航”——让每一个读图的人都能快速找到自己在系统里的位置以及想去的地方在哪里。C4 模型只是给了这套导航系统一个标准的地图比例尺剩下的靠的是画图的人和看图的人之间那一点点默契。希望这篇文章能帮你也建立起这份默契。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →