C4 模型示例:Stripe、Netflix、Uber 和 Revolut

你能找到的大多数 C4 模型示例都只是一个玩具系统:一家银行、一个网店、一个只有三个方框加一个数据库的网上银行应用。它们展示了记法,却没有展示真正困难的部分:当真实系统有几百个服务时,决定该省略什么。

本文收集了四个规模更大的 C4 模型示例,每一个都从上下文一直建模到组件:一次 Stripe 刷卡扣款、一次 Netflix 播放请求、一次 Uber 叫车请求,以及一次 Revolut 跨境转账。每个示例都附有第 1 层图、一段关于如何放大到第 2 层和第 3 层的简短说明,以及完整文章的链接。文末还有一个可以直接复制的小示例,以及这四个示例的共同点。

关于资料来源。 这四个示例都来自我们的"Anatomy of"系列,每一个都完全基于相关公司的公开资料:工程博客、技术大会演讲、开源仓库、招聘信息和外部案例研究。它们都不是相关公司的官方架构文档。每篇完整文章都标明了哪些细节是我们推断的,而不是公司明确说明的。

如果你对 C4 模型本身还不熟悉,请先阅读 C4 模型是什么。四篇分层指南会更深入地讲解每种图:系统上下文图、容器图、组件图和代码图。

怎样才算好的 C4 示例

一个 C4 图示例有用,是因为你能从中学到决策,而不只是形状。下面四个示例遵循同样的规则,在看任何一个之前,这些规则都值得先拿走。

追踪一个用户操作。 这些示例没有一个试图记录整个公司。每个示例都选取用户做的一件事(一次扣款、一次播放、一次叫车、一次转账),只画出这个操作涉及的部分。正是这一点,让一个拥有上千个服务的系统能收拢成一页可读的图。

第 1 层控制在十个方框左右。 在系统上下文层面,像 Netflix 这样的公司并不是一千个微服务,而是少数几个产品系统和围绕它们的外部参与者。如果你的第 1 层图需要四十个方框,说明边界画在了错误的高度。

第 2 层只放大一条路径。 容器图展示这一个操作路径上的容器,并标注技术和协议。它不会展示公司运行的所有容器。

第 3 层的放大要有理由。 只有一个容器会画组件图,要么是有意思的工程所在之处,要么是公司公开资料足够详尽、能如实建模的那一个。

用决策来解释方框。 每个示例都在各层之间放了三条简短的架构决策记录(ADR)。图展示存在什么,ADR 说明为什么是这个样子——这正是新人最先会问的问题。

四个示例的对比一览:

示例 追踪的操作 第 1 层 第 2 层放大 第 3 层放大
Stripe 一次刷卡扣款(PaymentIntents.create()) 15 个产品系统 Payments core 幂等层
Netflix 按下播放 10 个系统 Streaming Platform Cosmos 视频流水线
Uber 一次叫车请求,从点击到司机接单 3 个产品平台、1 个 Marketplace、Foundation 平台 Marketplace 派单路径 DISCO 匹配引擎
Revolut 一笔欧元转英镑的转账 8 个产品系统 Retail Banking 中的转账路径 Sherlock 反欺诈引擎

示例 1:Stripe,一次刷卡扣款

Stripe C4 系统上下文图:15 个系统与外部参与者

基于 Stripe 的公开资料。并非 Stripe 官方架构文档。

第 1 层。 在系统上下文层面,Stripe 并不是"一个支付 API"。我们的模型呈现出共享同一底座的十五个产品系统:Payments、Connect、Billing、Radar、Issuing、Treasury 等。围绕它们的是商户、持卡人、卡组织、替代支付方式、收单行和发卡行、银行合作伙伴以及 AWS。

第 2 层。 容器图打开 Payments 方框,追踪一次 PaymentIntents.create() 调用:API 网关、位于每个写操作端点之前的幂等层、PaymentIntent 状态机、卡数据保险库、并行运行的 Radar 风险评分、卡组织连接器、账本以及 Webhook 投递。

第 3 层。 组件放大进入幂等层内部,因为这是整个技术栈中公开资料最详尽的部分:请求哈希器、PostgreSQL 中的键存储、阶段执行器,以及一个恢复点追踪器,让重试的请求能从中断处继续执行。

可以借鉴什么。 想学组件图,就研究这个示例。它的第 3 层视图不是一串类名,而是读者可以推理的一连串步骤("每个外部副作用都位于两个恢复点之间")。

完整文章:Anatomy of a Charge:用 C4 建模 Stripe。

示例 2:Netflix,一次播放请求

Netflix C4 系统上下文图:10 个系统与外部参与者

基于 Netflix 的公开资料。并非 Netflix 官方架构文档。

第 1 层。 从 Netflix 的公开资料中,可以一致地梳理出十个系统:Member Experience、Content Discovery、Streaming Platform、Open Connect、Studio Engineering、Content Engineering、Data Platform、Cloud Platform、Security 和 Ads Platform。外部参与者包括会员设备、托管 Open Connect 设备的 ISP、AWS、DRM 提供商、支付合作伙伴和内容制作公司。

第 2 层。 容器放大打开 Streaming Platform,追踪一次播放请求:Playback API、清单服务、负责 DRM 的许可证服务、消息安全层、EVCache 缓存层以及其后的 Cassandra 集群。清单把设备指向一台 Open Connect 设备,自建 CDN 这一第 1 层决策在这里再次出现。

第 3 层。 组件视图进入视频编码流水线 Cosmos。它并不在请求时的播放路径上(编码发生在影片入库时),文章里也明确说明了这一点。之所以选它,是因为 Netflix 公开的资料足以给每一步命名:检查、复杂度分析、码率阶梯生成、编码、校验和质量评分。

可以借鉴什么。 这是第 1 层"十个而不是一千个"最清晰的示范,也是一个好例子:当第 3 层的放大离开了所追踪的路径时,坦率承认。

完整文章:Anatomy of a Play:用 C4 建模 Netflix。

示例 3:Uber,一次叫车请求

Uber C4 系统上下文图:Mobility、Delivery、Freight、Marketplace

基于 Uber 的公开资料。并非 Uber 官方架构文档。

第 1 层。 在系统上下文层面,Uber 是建立在一个共享 Marketplace 之上的三个产品平台(Mobility、Delivery、Freight),下面还有一层 Foundation 平台:Maps、Payments、身份与风控、通信、机器学习平台、工作流引擎、存储、流处理、可观测性和计算。外部参与者包括乘客、司机、点餐用户、配送员、商家、支付网络、电信运营商和城市监管机构。

第 2 层。 容器放大沿着 Marketplace 的派单路径追踪一次叫车请求:边缘网关、将每次行程作为一个工作流实例运行的行程编排器、匹配引擎、动态定价、ETA 服务、司机状态、存储层以及 Kafka。

第 3 层。 组件视图打开匹配引擎:把坐标转换为 H3 六边形索引的请求归一化器、运力扫描器、环形扩展器、候选排序器、分配求解器、通知分发器,以及一条降级路径。

可以借鉴什么。 这个示例展示了如何在第 1 层画一家平台型公司,而不必画出每一个产品。位于中间的共享 Marketplace,比任何服务清单都更能说明 Uber 的架构。

完整文章:Anatomy of a Ride:用 C4 建模 Uber。

示例 4:Revolut,一笔跨境转账

Revolut C4 系统上下文图:产品系统与外部参与者

基于 Revolut 的公开资料。并非 Revolut 官方架构文档。

第 1 层。 公开资料描述了至少八个产品系统:Retail Banking、Business Banking、FX、Wealth and Trading、Credit、FinCrime、Onboarding and KYC,以及带事件总线的核心账本。围绕它们的是卡组织、支付通道(Faster Payments、SEPA、SWIFT)、合作银行、监管机构和 Google Cloud。这张图让一件组织架构图看不出来的事情一目了然:每个产品都有箭头指向 FinCrime,所以它位于所有产品的关键路径上。

第 2 层。 容器放大追踪一笔经由 Retail Banking 的欧元转英镑转账:移动应用、API 边缘层、带显式状态机的转账编排器、基于 PostgreSQL 的复式记账账本、FX 引擎、FinCrime 流水线、每条支付通道各一个的连接器、事件存储以及通知。

第 3 层。 组件视图打开反欺诈引擎 Sherlock:特征组装、内存画像存储、模型服务器、决策策略、夜间重新训练,以及分析师反馈闭环。

可以借鉴什么。 这是四个示例中最完整的一个。它在第 1 层和第 2 层之间加入了分步时间线(延迟数据明确标注为示意值,而非公开数据),还有一节"照抄什么、跳过什么",告诉一个只有十个服务的团队哪些决策值得照搬、哪些不该。

完整文章:Anatomy of a Transfer:用 C4 建模 Revolut。

一个可以直接复制的小示例

上面四个示例是有意选的大系统。大多数系统没那么大,所以这里给出一个涵盖三个实用层级的小型 C4 模型示例:它就是我们在 C4 模型完整指南中通篇使用的电商平台。照搬结构,改掉方框名即可。

第 1 层:系统上下文

[Customer] --> [E-Commerce Platform] : Browses products, places orders
[Warehouse Staff] --> [E-Commerce Platform] : Manages inventory
[E-Commerce Platform] --> [Payment Gateway (Stripe)] : Processes payments
[E-Commerce Platform] --> [Shipping Provider (FedEx API)] : Creates shipments
[E-Commerce Platform] --> [Email Service (SendGrid)] : Sends notifications

两类用户、三个外部系统,以及一个代表你所拥有的一切的方框。

第 2 层:容器

[Single-Page Application (React)] --> [API Gateway (Kong)] : Makes API calls (HTTPS/JSON)
[API Gateway] --> [Order Service (Go)] : Routes requests
[API Gateway] --> [Product Service (Go)] : Routes requests
[API Gateway] --> [User Service (Go)] : Routes requests
[Order Service] --> [Order Database (PostgreSQL)] : Reads/writes orders
[Product Service] --> [Product Database (PostgreSQL)] : Reads/writes products
[User Service] --> [User Database (PostgreSQL)] : Reads/writes users
[Order Service] --> [Message Queue (Kafka)] : Publishes order events
[Notification Service (Go)] --> [Message Queue] : Consumes order events

每个方框都标明技术,每条箭头都标明协议或用途,数据存储也画成容器。

第 3 层:组件(Order Service 内部)

[Order Handler] --> [Order Service] : Delegates business logic
[Order Service] --> [Order Repository] : Persists orders
[Order Service] --> [Payment Client] : Validates payment
[Order Service] --> [Inventory Client] : Checks stock availability
[Order Repository] --> [Order Database (PostgreSQL)] : SQL queries
[Payment Client] --> [Payment Gateway (Stripe)] : HTTPS/REST
[Inventory Client] --> [Product Service] : gRPC

只有一个容器画了组件图,和四个大示例遵循的规则相同。Product 服务和 User 服务只是简单的 CRUD,画出它们的内部,并不会比一份目录清单多告诉你什么。

每个选择背后的理由,请参阅分层指南:系统上下文图应该包含什么、容器图应该包含什么,以及什么时候值得画组件图。这里跳过了第 4 层,原因和大多数团队一样;代码图指南解释了它在什么时候物有所值。

四个示例的共同点

把四个示例放在一起,会发现它们遵循同样的几个习惯。这些都不是 C4 模型本身的规则,而是让这些模型易于阅读的做法。

第 1 层大约十个方框。 Stripe 十五个,Netflix 十个,Revolut 八个,Uber 则是一个 Marketplace 上的三个产品平台加底下的 Foundation 平台。这些公司没有一家是小公司。第 1 层图之所以能保持小巧,是因为它们按产品系统而不是按服务来分组。

第 2 层一条路径。 每张容器图只展示一个操作经过的容器。Stripe 的容器视图里没有 Billing 或 Atlas 的容器,Netflix 的视图里也没有任何 Studio Engineering 的内容。这不是遗漏:那些容器属于另一个操作的另一张图。

第 3 层一个容器,诚实地选择。 组件放大总是去往公开文档足够深入、能画出真实组件的地方。Netflix 那篇文章直接说明了 Cosmos 在请求时并不在播放路径上。一个把这种选择藏起来的示例,教给人的是错误的东西。

外部系统和内部系统同样重要。 卡组织、ISP、支付通道、电信运营商:在四个示例中,最重要的方框里总有几个是公司自己并不拥有的。Revolut 的支付通道连接器就是那种无法靠工程手段消除故障的容器,而图正是让这一点显现出来的东西。

决策紧挨着方框。 每个示例都有三条 ADR,每条 ADR 都解释了一个会让新人感到意外的方框:为什么别的流媒体服务用商业 CDN,而 Netflix 有 Open Connect;为什么 Revolut 没有 Kafka;为什么 Stripe 选择在 MongoDB 之上构建,而不是迁移出去。如果你想知道怎么写这些 ADR,可以看架构决策记录完整指南。

每个方框都有负责人。 每篇文章都在模型最后给出一张归属图:哪个团队拥有哪个系统或容器。正是这一步,让一张图变成有人负责保持其准确的东西。

为你自己的系统建模

要应用这些方法,你并不需要 Stripe 的交易量或 Uber 的服务数量。同样的步骤也适用于一个只有十个服务的系统:

  1. 选一个重要的用户操作:结账、注册,或者那个会在凌晨 3 点把人叫起来的东西。
  2. 画第 1 层:把你的系统画成一个方框,加上每一类用户,以及这个操作涉及的每一个外部系统。争取少于十五个方框。
  3. 为这一个操作画第 2 层:只画它经过的容器,每个都标明技术,每条箭头都标明动词和协议。
  4. 选一个容器画第 3 层:选新人最难弄懂的那一个,画出它的主要组件。
  5. 写三条 ADR:针对别人最可能问"为什么是这样?"的三个方框。
  6. 给每个容器写上团队名。

然后对下一个操作重复。做完三四个操作后,第 2 层的图会开始重叠,而重叠的部分就是你真正的容器图。

这四个示例无法展示的,是六个月之后会发生什么:代码已经变了,图却没有变。archyl 正是围绕这个问题打造的。连接一个仓库,AI 发现会提出系统、容器、组件和关系,由你来审批,而不必从零开始画;之后,漂移评分会持续将模型与代码比对,模型一旦过时你就会知道。

常见问题

什么是好的 C4 模型示例?

好的 C4 模型示例会沿着一个真实的用户操作从第 1 层追踪到第 3 层,并解释其中让人意外的方框。本文的四个示例(Stripe、Netflix、Uber、Revolut)都做到了这一点:第 1 层图大约十个系统,容器图限定在一条路径上,外加一次组件放大。对于小系统,上面的电商示例是一个合适的模板。

哪里可以找到 C4 容器图示例?

四篇完整文章各有一张第 2 层容器图:Stripe 的 Payments core、Netflix 的 Streaming Platform、Uber 的派单路径和 Revolut 的转账路径。如果想看一个带容器和关系表格的小型实战示例,请参阅 C4 容器图指南。

这些是 Stripe、Netflix、Uber 和 Revolut 的官方架构图吗?

不是。每个模型都完全基于相关公司的公开资料,每篇完整文章都标明了哪些细节是推断的,而不是公司明确说明的。它们的目的是展示 C4 模型如何让复杂的技术栈变得易读,而不是记录这些公司如今的实际运行方式。

C4 示例需要全部四个层级吗?

很少需要。这里的四个示例都只画了第 1、2、3 层就停止了。代码层的图会随着每次重构而变化,通常从代码生成比手工绘制更好,这也是大多数真实的 C4 模型止步于组件层的原因。

C4 系统上下文图应该有多少个元素?

没有官方上限。在这些示例中,对于拥有成百上千个服务的公司,第 1 层包含八到十五个系统以及它们的外部参与者。如果你的图需要多得多的元素,很可能是在错误的层级画了容器,或者你需要一张跨多个系统的系统全景(system landscape)视图。


想用 C4 为你自己的系统建模?在 Developer 计划下免费试用 archyl,无需信用卡。继续阅读:什么是 C4 模型?完整指南 | C4 系统上下文图指南 | C4 容器图指南 | C4 组件图指南 | C4 代码图指南