用 C4 模式在 PlantUML 里画清晰架构图

puml.online

C4 是一种给架构图「固定分镜」的方法论,PlantUML 是它的最佳载体。这篇是从入门到上手 C4-PlantUML 的最短路径。

什么是 C4

C4 由 Simon Brown 提出,把架构图分成 4 层:

层级 视角 读者
Level 1 — System Context 系统对外提供什么价值 任何利益相关方
Level 2 — Container 系统内部由哪些「可独立部署」单元组成 架构师、运维
Level 3 — Component 容器内由哪些模块组成 开发
Level 4 — Code 模块内部的类图(可选) 开发

C4 的核心思想:没有一种图能讲清楚所有细节,靠分层逐步放大

工具链:C4-PlantUML

C4 在 PlantUML 里有官方实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

Person(user, "用户", "通过浏览器使用")
System_Boundary(ecommerce, "电商系统") {
Container(web, "Web App", "React", "用户操作界面")
Container(api, "API Server", "Go", "对外 REST API")
ContainerDb(db, "Primary DB", "PostgreSQL", "订单 / 用户主库")
Container(redis, "Cache", "Redis", "热点缓存")
}

System_Ext(payment, "支付网关", "第三方支付")

Rel(user, web, "使用")
Rel(web, api, "JSON/HTTPS")
Rel(api, db, "读 / 写")
Rel(api, redis, "查询")
Rel(api, payment, "扣款")
@enduml

C4-PlantUML 在普通 PlantUML 之上提供:

  • Person / System / System_Ext 表示人或外部系统
  • Container / ContainerDb / ContainerQueue 表示容器
  • Component / ComponentDb 表示容器内部模块
  • Rel / Rel_Back 表示关系
  • System_Boundary / Container_Boundary 表示边界

内网部署:vendor 进来

!include 远程 URL 在企业内网经常超时。建议本地 vendor:

1
2
# 把整个仓库 clone 到 vendor/
git clone --depth=1 https://github.com/plantuml-stdlib/C4-PlantUML.git source/plantuml/c4

然后:

1
2
3
@startuml
!includeurl https://raw.githubusercontent.com/.../C4_Container.puml ❌
!include /plantuml/c4/C4_Container.puml ✅

CI 里加进 build 流程,确保 vendor 文件同步更新。

完整示例:4 层 C4

Level 1 — System Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@startuml
!include /plantuml/c4/C4_Context.puml

LAYOUT_TOP_DOWN()

Person(visitor, "访客")
Person(admin, "运营")

System(blog, "博客系统", "用户写博客 + 阅读")

System_Ext(cdn, "CDN", "静态文件分发")
System_Ext(sso, "SSO 认证", "统一登录")

Rel(visitor, blog, "浏览 / 写作")
Rel(admin, blog, "管理")
Rel(blog, cdn, "拉取静态资源")
Rel(blog, sso, "用户认证")
@enduml

Level 2 — Container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@startuml
!include /plantuml/c4/C4_Container.puml

Person(visitor, "访客")

System_Boundary(blog, "博客系统") {
Container(web, "Web", "Next.js", "SSR + 静态导出")
Container(api, "API", "Node.js", "REST 接口")
ContainerDb(mysql, "Blog DB", "MySQL", "文章 / 评论")
ContainerDb(redis, "Cache", "Redis", "阅读量 / 限流")
}

System_Ext(cdn, "CDN")
System_Ext(sso, "SSO")

Rel(visitor, web, "https")
Rel(web, api, "JSON")
Rel(api, mysql, "SQL")
Rel(api, redis, "缓存读写")
Rel(web, cdn, "拉静态")
Rel(api, sso, "Token 验证")
@enduml

Level 3 — Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@startuml
!include /plantuml/c4/C4_Component.puml

Container_Boundary(api, "API Server") {
Component(auth_ctrl, "AuthController", "Express", "登录 / 鉴权")
Component(article_ctrl, "ArticleController", "Express", "增删改查")
Component(comment_ctrl, "CommentController", "Express", "评论")
Component(article_repo, "ArticleRepo", "TypeORM", "数据访问")
Component(comment_repo, "CommentRepo", "TypeORM", "数据访问")
Component(cache, "Cache", "ioredis", "缓存接口")
}

Rel(auth_ctrl, article_repo, "查询用户")
Rel(article_ctrl, article_repo, "读写")
Rel(article_ctrl, cache, "读缓存")
Rel(comment_ctrl, comment_repo, "读写")
Rel(comment_ctrl, cache, "读缓存")
@enduml

Level 4 — Code(可选)

这一层用 UML 类图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@startuml
class Article {
+id: int
+title: string
+content: text
+author: User
+comments: Comment[]
}
class User {
+id: int
+name: string
+email: string
}
class Comment {
+id: int
+body: text
+author: User
}
Article "1" *-- "*" Comment : has
Article "*" --> "1" User : author
@enduml

C4 跟普通组件图的区别

维度 普通组件图 C4
表达层级 单层,自由 4 层固定
边界表达 自由画法 System_Boundary 标准
关系类型 任意 Rel 标准化 + 标签
渲染器 原生 PlantUML 需 C4-PlantUML stdlib
学习曲线 中(先学 4 层概念)
适用 简单草稿 团队长期维护

C4 的最大价值是不同人画出来形态接近,团队沟通成本降低。

实用小贴士

Lay out 方向控制

默认是上下流,但有时你想左右:

1
2
3
4
LAYOUT_TOP_DOWN()        ! 默认
LAYOUT_WITH_LEGEND() ! 加图例
LAYOUT_AS_SKETCH() ! 手绘风格
SHOW_DYNAMIC_LEGEND() ! 动态图例

颜色和主题

1
2
UpdateElementStyle("c4model:boundary", $bgColor="#FAFAFA", $borderColor="#2F4858")
UpdateRelStyle(RelLabelColor="#2F4858")

暗色背景:

1
2
UpdateElementStyle("c4model:boundary", $bgColor="#161B22", $borderColor="#58A6FF")
UpdateElementStyle("c4model:person", $bgColor="#21262D", $fontColor="#E6EDF3")

一图多 zoom

Level 2 的容器图可以加 LAYOUT_TOP_DOWN() + LAYOUT_WITH_LEGEND(),多 zoom 也好读。

我团队的实践

4 层图各放一个文件:

1
2
3
4
5
docs/architecture/
c4-level-1.puml
c4-level-2.puml
c4-level-3.puml
diagrams.puml

CI 自动渲染 SVG:

1
2
3
4
5
6
# .github/workflows/uml.yml
- run: |
curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar
find docs/architecture -name '*.puml' | while read f; do
java -jar plantuml.jar -tsvg -nometadata "$f"
done

每张图渲染好就 commit 进仓库。Onboarding 时新人看着 Level 1/2/3 三张图,5 分钟了解整个系统全貌。

常见错误

  • 把 Level 1 画复杂:Context 图只需要 3-6 个元素。超了就变 Level 2。
  • 每张图都画 4 个 Level:挑重点。普通小型系统 Level 2 就够;复杂分布式才上 Level 3。
  • 关系不标标签:所有 Rel(A, B) 都不写标签,看不出 A 和 B 交互什么。
  • Container 选错:Container 是「可独立部署 / 独立进程」单位。一个 Go binary 是一个 Container,一个进程内的协程不是。
  • 忘了 vendor:远程 !include 在企业内网常常失败,CI 一次失败就让你学乖。

踩坑总结

  • C4-PlantUML 的最新 tag 不一定是 GitHub 默认分支。我建议固定到 v2.x tag。
  • ShowPerson / HidePerson 控制 Person 的显示级别,复杂图里用来去掉非关键用户。
  • AddRelTag 给关系加标签:AddRelTag("async", $lineColor="#888", $lineStyle=DashedLine),渲染时 Rel(..., "异步") 应用此 style。

何时不上 C4

  • 画流程图 / 状态图:用时序图或状态图,不归 C4 管。
  • 画一张营销 PPT 插图:C4 太规整,视觉冲击力不够。
  • 图只要画 1-2 个元素:C4 太正式了,单元素图会用 <system>_name + 一根箭头 就够了。

一句话总结

C4 是给架构图立规矩的方法论,PlantUML 是它的好载体,4 层抽象让团队画的图都长得像,能互相看懂的图才是有价值的图。

  • 标题: 用 C4 模式在 PlantUML 里画清晰架构图
  • 作者: puml.online
  • 创建于 : 2026-07-29 14:50:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-c4-architecture/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。