什么是C4模型?软件团队完全指南
软件架构图有一个声誉问题。它们要么太抽象以至于没用,要么太详细以至于无法维护。你可能都见过:一个标着"系统"的单框图什么也没说清楚,还有一个包含200个类的庞大UML图,告诉你一切但唯独不包含你真正需要知道的东西。
C4模型由Simon Brown创建,借鉴了制图学的一个理念来解决这个问题。地图在多个缩放级别上都能工作。你从世界地图开始把握方位,然后放大到一个国家,再到一个城市,再到一条街道。每个级别都展示了适合你所提问题的恰当详细程度。C4模型将同样的原理应用于软件架构。
在本指南中,我们将全面介绍C4模型的所有内容:每个层级代表什么、何时使用、常见陷阱,以及现代团队如何在实践中实施C4。
C4代表什么?
C4代表模型定义的四个抽象层级:
- Context(上下文) -- 全局视图。你的系统如何融入整个世界。
- Containers(容器) -- 系统的高层技术构建块。
- Components(组件) -- 每个容器的内部结构。
- Code(代码) -- 实际的实现细节。
每个层级为不同的受众回答不同的问题。产品经理关心上下文。平台工程师关心容器。负责特定服务的开发者关心组件。没人真正需要手动绘制代码层级——稍后会详细说明。
C4的力量不在于任何一张图。它在于层级体系。一个层级上的每个元素都在下一层级分解为一张图。容器图中标注为"后端API"的方框变成了自己的组件图,展示内部模块。这创建了一个可导航、可缩放的架构视图。
第1层:系统上下文图
系统上下文图是任何C4模型的起点。它回答一个问题:你的系统如何融入更广泛的环境?
它展示什么
- 你的软件系统作为中心的一个方框
- 使用它的人员(参与者、角色、用户画像)
- 它集成的外部系统
- 所有这些之间的关系
它不展示什么
- 系统的内部结构
- 技术选型
- 数据库、队列或基础设施
- 部署细节
真实示例
假设你正在记录一个电商平台。你的系统上下文图会展示:
[客户] --> [电商平台] : 浏览商品,下订单
[仓库员工] --> [电商平台] : 管理库存
[电商平台] --> [支付网关 (Stripe)] : 处理支付
[电商平台] --> [物流提供商 (FedEx API)] : 创建发货
[电商平台] --> [邮件服务 (SendGrid)] : 发送通知
五个用户和外部系统。一个方框代表你的整个平台。就这些。
为什么这个层级重要
系统上下文图迫使你明确边界。你拥有什么?你依赖什么?你的用户是谁?这些问题看似显而易见,但团队经常难以一致地回答它们。
这也是你展示给非技术利益相关者的图。你的CEO可以看着它理解系统做什么、谁在使用它、它依赖什么第三方服务。试试用UML类图做到这一点。
好的系统上下文图的技巧
- 保持在一页之内。如果放不下,你的系统边界可能有问题。
- 用动词短语标注每个关系:"通过...发送邮件"、"通过...处理支付"、"从...读取库存"。
- 包含所有外部系统,即使是你认为理所当然的(DNS、CDN、监控)。
- 不要包含内部细节。抵制这种诱惑。
第2层:容器图
容器图放大到你的系统内部,展示其高层技术构建块。在C4术语中,"容器"是任何可单独部署或运行的单元——不一定是Docker容器。
什么算作容器
- Web应用(React SPA、Next.js应用)
- 移动应用(iOS应用、Android应用)
- 后端API服务(Go API、Node.js服务器)
- 数据库(PostgreSQL、MongoDB、Redis)
- 消息代理(Kafka、RabbitMQ)
- 文件存储系统(S3、MinIO)
- 无服务器函数(AWS Lambda、Cloud Functions)
每个容器运行在自己的进程中或拥有自己的数据存储。这是区分因素。两个一起部署的Go包是同一个容器的一部分。两个独立部署的Go服务是不同的容器。
真实示例
放大我们的电商平台:
[单页应用 (React)] --> [API网关 (Kong)] : 发起API调用 (HTTPS/JSON)
[API网关] --> [订单服务 (Go)] : 路由请求
[API网关] --> [商品服务 (Go)] : 路由请求
[API网关] --> [用户服务 (Go)] : 路由请求
[订单服务] --> [订单数据库 (PostgreSQL)] : 读写订单
[商品服务] --> [商品数据库 (PostgreSQL)] : 读写商品
[用户服务] --> [用户数据库 (PostgreSQL)] : 读写用户
[订单服务] --> [消息队列 (Kafka)] : 发布订单事件
[通知服务 (Go)] --> [消息队列] : 消费订单事件
现在你可以看到技术选型、通信模式和数据存储。架构师可以讨论是否合并订单和商品数据库。DevOps工程师可以规划部署拓扑。新开发者可以理解React前端在哪里结束、Go后端从哪里开始。
好的容器图的技巧
- 在括号中包含技术:"订单服务 (Go)"、"数据库 (PostgreSQL)"。
- 在关系上标注通信协议:"HTTPS/JSON"、"gRPC"、"AMQP"。
- 如果你有超过15-20个容器,为不同的子系统创建多个容器图。
- 包含数据库和队列。它们也是容器。
- 不要在这里深入到容器级别以下。内部模块属于组件图。
第3层:组件图
组件图放大到单个容器内部,展示其内部结构构建块。组件是容器内的主要抽象——可以理解为包、模块、服务或层。
什么算作组件
- HTTP处理器或控制器
- 业务逻辑服务
- 仓储或数据访问层
- 外部API客户端
- 后台任务处理器
- 中间件或拦截器
组件是逻辑分组,不一定是单个文件。OrderHandler组件可能由多个文件实现,但在概念上它是一个东西:系统中处理与订单相关的HTTP请求的部分。
真实示例
放大到订单服务容器内部:
[订单处理器] --> [订单服务] : 委托业务逻辑
[订单服务] --> [订单仓储] : 持久化订单
[订单服务] --> [支付客户端] : 验证支付
[订单服务] --> [库存客户端] : 检查库存
[订单仓储] --> [订单数据库 (PostgreSQL)] : SQL查询
[支付客户端] --> [支付网关 (Stripe)] : HTTPS/REST
[库存客户端] --> [商品服务] : gRPC
加入这个团队的开发者可以立即看到订单服务的结构:请求通过处理器进入,业务逻辑在服务中,数据访问通过仓储,外部调用通过专用客户端。
何时创建组件图
并非每个容器都需要组件图。在以下情况下创建:
- 容器足够复杂,新开发者难以浏览
- 有重要的设计模式(六边形架构、CQRS)需要图表来明确展示
- 多个团队共同开发同一个容器,需要对其结构有共同理解
在以下情况下跳过组件图:
- 容器很简单(一个只有三个端点的CRUD服务)
- 代码结构从文件夹布局就能看出
- 容器是第三方工具(Redis、Kafka),你无法控制其内部
第4层:代码图
代码层展示实际的实现细节:类、接口、函数及其关系。这本质上是UML类图或实体关系图。
关于第4层的实话
Simon Brown本人建议不要手动创建代码图。原因如下:
- 它们变化太频繁。每次重构都会使它们失效。
- 维护成本高。手动绘制类图很繁琐。
- 它们复制了代码中已经存在的信息。
- 现代IDE可以按需生成它们。
如果你需要代码级别的图表,请使用支持你语言的工具从源代码生成。不要手动绘制。
代码图真正有用的时候
有一些例外:
- 受益于可视化演示的复杂算法
- 设计模式(策略、观察者、状态机),其结构本身就是有趣的部分
- 你想要记录契约的公共API接口
- 用于教学模式的面试或入职材料
在实践中,大多数团队使用C4的三个层级(上下文、容器、组件),将代码层留给IDE生成的视图。
补充图
除了四个核心层级之外,Simon Brown还定义了几种补充图,作为C4层级体系的补充:
系统全景图
系统上下文图进一步缩小。它展示企业中所有的软件系统以及它们之间的关系。对管理系统组合的企业架构师很有用。
部署图
将容器映射到基础设施。展示哪些容器运行在哪些服务器上、在哪些云区域、在哪些负载均衡器后面。对DevOps和平台团队至关重要。
动态图
展示元素在运行时如何协作以完成特定用例。类似于UML序列图但使用C4符号。对记录复杂流程(如"当用户下订单时会发生什么")很有用。
C4模型与其他方法的比较
C4 vs. UML
UML定义了14种图类型。大多数团队使用其中2-3种,而且经常不一致。C4给你4个层级,目的明确。它更容易学习、使用和维护。
话虽如此,C4和UML并不互斥。你可以在第4层使用UML类图,或者使用UML序列图作为动态图。C4提供层级体系;UML在需要时提供具体符号。
C4 vs. Arc42
Arc42是架构文档的模板。它涵盖的内容远不止图表:质量需求、约束、风险、部署视图等。C4专注于层级化图表。许多团队两者都用:Arc42作为文档结构,C4作为其中的图表方法。
C4 vs."用白板就行"
白板非常适合探索和头脑风暴。但作为文档来说很糟糕。白板图会被擦掉、拍照质量差,且永远不会更新。C4提供了将白板探索转化为持久文档的结构。
采用C4时的常见错误
试图一次展示所有内容
C4的核心理念是渐进式展示。如果你的容器图展示了单个类,你就把层级体系压扁了。每张图应该只展示一个缩放级别的元素。
忽略关系
没有箭头的方框是清单,不是架构。关系——谁调用谁、使用什么协议、什么数据在它们之间流动——通常比方框本身更有价值。务必标注你的关系。
变成一个人的活动
架构文档是团队运动。如果只有一个人创建和维护图表,它们只会反映那一个人的理解(或误解)。作为团队一起审查C4图,最好作为架构审查流程的一部分。
不链接到其他文档
C4图在与其他工件连接时会更有力量。将容器链接到部署手册。将组件链接到架构决策记录(ADR)。将外部系统链接到API文档。孤立的图表不如连接的图表有价值。
让图表过时
任何文档方法的最大杀手是陈旧。描述去年架构的图比没有图更糟糕,因为它会主动误导人。将图表更新纳入你的工作流程——使其成为PR检查清单、冲刺回顾或架构会议的一部分。
Archyl如何实施C4模型
Archyl围绕C4模型作为一等概念构建。以下是每个层级如何映射到平台中的功能:
Archyl中的系统上下文
当你在Archyl中创建项目时,你定义系统及其与外部参与者的关系。系统上下文视图从你的模型自动渲染——无需手动绘制。添加系统、添加外部参与者、绘制关系,图表实时更新。
Archyl中的容器图
在每个系统内,你定义容器及其技术栈。Archyl渲染容器图,并允许你深入任何容器查看其组件。容器之间的关系展示通信协议和数据流。
Archyl中的组件图
在每个容器内,你定义组件。Archyl通过其代码元素功能将这些组件映射到实际代码,该功能将组件链接到仓库中的特定文件和目录。图表与代码之间的这种连接是实现偏移检测的关键。
AI驱动的发现
Archyl与绘图工具的不同之处在于,你不必手动构建模型。连接你的仓库,运行AI发现,Archyl会从你的代码库生成C4模型草稿。AI通过分析代码结构、配置文件和依赖图来识别系统、容器、组件和关系。
你审查建议,接受或修改它们,就能在几分钟而不是几周内获得C4模型。
偏移检测
一旦你的C4模型存在,Archyl会持续检查它是否仍然反映现实。偏移分数告诉你文档化架构中实际存在于代码库中的百分比。如果有人重命名了服务或删除了组件,偏移分数下降,你就知道文档需要更新。
这是大多数C4工具忽略的差距。创建图表是容易的部分。保持其准确性是困难的部分。Archyl的偏移检测弥补了这一差距。
开始使用C4:分步方法
如果你的团队刚接触C4模型,以下是实用的采用路径:
第1周:系统上下文
召集团队进行一小时的工作坊。将你的系统画成一个方框。识别每个用户角色和外部系统。绘制关系。你会惊讶这个简单练习引发了多少讨论——以及它创造了多少共识。
第2周:容器图
将上下文图中的系统方框分解为容器。有哪些可部署单元?存在什么数据库?有哪些消息代理或缓存?这是你让技术选型变得可见的地方。
第3-4周:关键容器的组件图
选择两三个最复杂的容器。将每个分解为组件。这是新开发者将花费最多时间的地方,所以优先处理最难理解的容器。
持续:维护和演进
初始创建是容易的部分。保持图表更新的纪律是区分从C4中获得价值的团队和一个月后就放弃的团队的关键。尽可能自动化。使用能检测偏移的工具。让图表审查成为开发工作流的一部分。
为什么C4有效
C4模型在技术上并非开创性的。层级分解和抽象层级自20世纪60年代以来就存在于计算机科学中。Simon Brown所做的是将这些理念打包成一个简单、易记的框架,具有明确的规则和最少的符号。
它有效是因为:
- 容易学习。 四个层级。方框和箭头。不需要UML认证。
- 可扩展。 从周末项目到企业平台,同样的四个层级都适用。
- 服务多种受众。 高管、架构师和开发者各自获得用他们语言说话的图表。
- 工具无关。 你可以使用白板、绘图工具、文本格式,或像Archyl这样的专用平台。
- 关注正确的事物。 结构和关系,而不是实现细节。
如果你团队的架构文档由过时的Visio文件、Slack中的白板照片和部落知识组成,C4模型是通往更好文档的最实用途径。
准备好构建你的C4模型了吗?免费试用Archyl,几分钟内从代码生成你的第一张架构图。或者探索更多:AI驱动的架构发现 | 架构即代码:用YAML定义你的C4模型 | 架构偏移分数。