C4 模型讲起来很简单,用错也很容易。很多团队最后画出一张巨型图,把用户、微服务、Java 类和云资源框全都混在一起,而这正是 C4 想要避免的。本文通过一个完整的 C4 模型架构图示例来讲解:一个虚构系统,分别在第 1、2、3 层画出来,让你看清每张图该放什么、给谁看,以及常见错误出在哪里。
一分钟了解 C4 模型
C4 模型由 Simon Brown 提出,用一组可以逐级缩放的图来描述软件架构。每一层面向不同的读者,回答不同的问题。
| 层级 | 图 | 回答的问题 | 主要读者 |
|---|---|---|---|
| 1 | System Context | 系统是什么、谁在用、依赖什么? | 所有人,包括非工程师 |
| 2 | Container | 有哪些可运行或可部署的部分,它们如何通信? | 工程师、架构师、SRE |
| 3 | Component | 某个容器内部有什么? | 负责该容器的开发者 |
| 4 | Code | 某个组件是如何实现的? | 开发者,最好由工具生成 |
C4 中的“container”是指任何独立运行或存储数据的单元:Web 应用、API、worker、数据库、消息队列或主题等,并不特指 Docker 容器。C4 定义的是抽象概念,而不是图形或颜色,所以表示法由你自己决定。官方参考见 c4model.com。
示例系统:在线书店 Folio
为了具体起见,我们使用一个虚构系统。Folio 是一家虚构的在线书店,并非真实公司,销售纸质书和电子书。
- 顾客浏览图书目录、购买图书、下载电子书。
- 后台员工管理目录、库存和价格。
- Folio 依赖三个外部系统:支付服务商、邮件发送服务,以及仓储合作伙伴的履约系统。
第 1 层:System Context 图
上下文图把 Folio 画成一个方框,并展示它周围的一切,不包含任何内部细节。
- Customer(Person):浏览并购买图书。
- Back-office Staff(Person):管理目录、库存和价格。
- Folio(Software System,在范围内):让顾客购买纸质书和电子书。
- Payment Provider(Software System,外部):授权并扣款银行卡支付。
- Email Service(Software System,外部):发送订单和发货邮件。
- Fulfilment System(Software System,外部,归仓储合作伙伴所有):拣货、打包并发出纸质书订单。
每条关系都要有一个说明意图的标签:
- Customer → Folio:“通过它浏览并购买图书”
- Back-office Staff → Folio:“通过它管理目录和库存”
- Folio → Payment Provider:“通过它收取银行卡付款”
- Folio → Email Service:“通过它发送订单邮件”
- Folio → Fulfilment System:“向其发送纸质书订单”
给谁看: 产品经理、新成员、安全评审人员、其他团队。如果读者需要看不止一眼才能理解,说明图太拥挤了。协议和技术通常不出现在这一层。
图表内容
- Customer
- Back-office Staff
- Folio
- Payment Provider
- Email Service
- Fulfilment System
- Browses and buys books using
- Manages catalogue and stock using
- Takes card payments using
- Sends order emails using
- Sends print orders to
第 2 层:Container 图
放大到 Folio 内部。人员和外部系统保持不变;Folio 变成一个 boundary,包住它的各个容器。
| Container | 技术 | 职责 |
|---|---|---|
| Web Storefront | React 单页应用 | 浏览器端的目录、购物车和结账界面 |
| Admin Portal | React 单页应用 | 供员工管理目录、库存和价格 |
| API Application | Java, Spring Boot | 通过 JSON/HTTPS API 提供目录、订单和账户逻辑 |
| Order Worker | Java, Spring Boot | 处理已支付订单,触发履约和邮件 |
| Order Events Queue | RabbitMQ 队列 | 将 OrderPaid 事件从 API 传递给 worker |
| Main Database | PostgreSQL | 顾客、目录和订单 |
| E-book Store | 对象存储 | 电子书文件,通过签名下载链接提供 |
此时关系要同时写明意图和协议:
- Web Storefront → API Application:“读取目录、下单 [JSON/HTTPS]”
- API Application → Main Database:“读写数据 [SQL/TCP]”
- API Application → Payment Provider:“创建并扣款 [HTTPS]”
- API Application → Order Events Queue:“发布 OrderPaid 事件 [AMQP]”
- Order Worker → Order Events Queue:“消费 OrderPaid 事件 [AMQP]”
- API Application → E-book Store:“为电子书文件的下载链接签名 [HTTPS]”
- Order Worker → Fulfilment System:“提交纸质书订单 [JSON/HTTPS]”
- Order Worker → Email Service:“发送确认邮件 [HTTPS]”
给谁看: 工程师、架构师和 SRE。在日常工作中,这通常是最有用的一张 C4 图:设计评审时投到屏幕上的、交给新同事的,往往就是它。注意图上没有什么:Pod、VPC、负载均衡器。它们应该放在部署图里。
图表内容
- Folio
- Customer
- Back-office Staff
- Web Storefront
- Admin Portal
- API Application
- Main Database
- Order Worker
- Order Events Queue
- E-book Store
- Fulfilment System
- Payment Provider
- Email Service
- Buys books [HTTPS]
- Manages stock [HTTPS]
- Places orders [JSON/HTTPS]
- Manages catalogue [JSON/HTTPS]
- Reads/writes [SQL]
- Payments [HTTPS]
- OrderPaid [AMQP]
- Consumes [AMQP]
- Print orders [JSON/HTTPS]
- Emails [HTTPS]
- Signs download links [HTTPS]
第 3 层:Component 图
放大到单个容器 API Application。不是每个容器都需要组件图;只在内部复杂到值得解释时再画。
- Catalogue Controller(Spring MVC REST 控制器):搜索和图书详情接口。
- Checkout Controller(Spring MVC REST 控制器):购物车和结账接口。
- Order Service(Spring 服务):校验购物车、创建订单、发布事件。
- Payment Gateway Client(Spring 组件):封装支付服务商的 API。
- Order Repository(Spring Data JPA):持久化订单。
- Catalogue Repository(Spring Data JPA):读取图书、价格和库存。
关系遵循与第 2 层相同的规则,例如 Order Service → Payment Gateway Client“请求扣款”,Order Repository → Main Database“[SQL/TCP]”。
给谁看: 负责该容器的开发者。组件图是最容易过时的一类图。
第 4 层 Code: 不要手工绘制。需要时用 IDE 生成类图即可。
让 C4 图易读的表示法技巧
- 每个方框包含四项信息: 名称、元素类型、技术、一句话职责。
API Application
[Container: Java, Spring Boot]
Provides catalogue, ordering and account functionality via a JSON/HTTPS API.
- 给每条关系加标签: 写明意图,从第 2 层开始还要写协议。只写“使用”的箭头对读者毫无信息量。
- 每条箭头只表示一个方向,从源端的角度描述。
- 添加图例: 解释形状、颜色、线型和缩写。虚线在你说明之前没有任何含义。
- 给每张图加标题: 写明类型和范围,例如“Container 图:Folio”。
- 画出范围内系统的 boundary,让读者看清哪些归你所有、哪些只是被调用。
常见错误
- 直接从组件开始画。 没有上下文图和容器图,读者不知道组件在哪里、为什么存在。
- 混用层级。 Person、Pod 和 Java 类出现在同一张图上,结果哪个都没讲清楚。
- 箭头没有标签。 没有意图和协议的连线只能让读者去猜,而且每个人猜得都不一样。
- 手工绘制 Code 层。 画完第二天就过时了。
- 在容器图里放基础设施。 Namespace、子网和实例类型应该放在部署图里。
补充图:部署图与动态图
C4 还定义了辅助视图,其中两种特别有用:
- 部署图(Deployment): 把容器映射到基础设施上。以 Folio 为例,API Application 和 Order Worker 可以作为 Deployment 运行在 Kubernetes 集群中,PostgreSQL 使用托管数据库,E-book Store 放在对象存储里。Kubernetes 和云的细节应该放在这里。
- 动态图(Dynamic): 用编号步骤展示一个具体流程,例如“顾客购买电子书”:1) Storefront 提交结账,2) API 完成扣款,3) API 发布 OrderPaid,4) worker 发送带下载链接的邮件。
在 Archboard 中绘制这个 C4 模型架构图示例
Archboard 提供 C4 Model 节点包,包含 Person、Software System、Container、Component 和 Database 节点以及 boundary。它们是带端口的一等节点,而不是通用图形,关系通过端口与端口相连。
- 使用 Google 或 GitHub 登录,新建一张图。
- 第 1 层: 放置两个 Person 节点(Customer、Back-office Staff)、一个用于 Folio 的 Software System 节点,以及三个分别用于支付、邮件和履约系统的 External System 节点。连接端口,并为每条连线标注意图。
- 第 2 层: 为 Folio 添加一个 boundary,为 Web Storefront、Admin Portal、API Application、Order Worker、Order Events Queue 和 E-book Store 放置 Container 节点,再为 Main Database 放置一个 Database 节点。填写名称、技术和职责,并为连线标注意图和协议。
- 第 3 层: 为 API Application 添加一个 boundary,在其中放置 Component 节点。
- 图例和说明: 用 Core 元素中的 Markdown 块或 Table 做图例,用 Frame 把每一层放在无限画布上各自的区域。
- 在旁边放部署视图: C4 视图可以和 Kubernetes、Cloud 节点包放在同一张画布上。为 API Application 和 Order Worker 画一个包含 Deployment、Service 和 Ingress 节点的 Namespace 框,紧挨着它所实现的容器视图。
由于 Archboard 是一块画布,每个视图都是有意识地画出来的:每一层放什么由你决定,C4 节点也可以和 Kubernetes、Cloud 节点包的节点放在同一个白板上,让部署视图紧挨着容器视图。
Free 方案中每张图可容纳 15 个图表节点和 150 个便签、文本、贴纸和思维导图主题,对 Folio 这样的系统来说,足够每张图画一个 C4 层级。如果想把所有层级放在一张画布上,Pro 方案取消了节点和图数量的限制。更多 C4 用法见 C4 使用场景。
免费模板
C4 容器视图示例 是一张公开的图,你可以用使用此示例把它复制到你的工作区。可以用它来安排 boundary 布局,然后按上文所述为每条连线标注意图和协议。
常见问题
四个层级都需要吗? 不需要。大多数团队从上下文图和容器图中获益最多。组件图按需添加,代码层视图只在需要时生成。
C4 的 container 就是 Docker 容器吗? 不是。它指任何可以单独运行或部署的单元,包括数据库和单页应用,可能运行在 Docker 中,也可能不是。
Kubernetes 细节放在哪里? 放在部署图里。容器图专注于职责和通信。