微服务架构图:一个示例,以及让它保持可读的七条规则

大多数微服务图最终都变成一团箭头;本文用一个完整示例和七条规则,帮你画出能看清入口、数据归属和服务间通信的图。

2026/10/22

微服务架构图:一个示例,以及让它保持可读的七条规则

让一个团队画出他们的微服务,你经常会得到同一张图:三十个方框,每个都和其他所有方框相连,箭头上没有标签。它很准确,却回答不了任何人关心的问题。工程师们管这叫“死星图”。

一张有用的微服务架构图,是一系列关于“展示什么”的决定。本文以一个有五个服务的在线商店为例,讲解让图保持可读的七条规则。无论你有五个服务还是五十个,这些规则都同样适用。

图需要回答哪些问题

动手画之前,先把问题写下来。一张好的微服务图能让人一眼看出这五个问题的答案:

  1. 请求从哪里进入? 客户端、API 网关、BFF(Backends for Frontends)。
  2. 哪个服务拥有哪些数据? 每个数据库都应该有且只有一个所有者。
  3. 谁同步调用谁? 这些调用会共担延迟和故障。
  4. 哪些事情异步发生? 事件和队列让服务在时间上解耦。
  5. 哪些不在我们的控制范围内? 支付服务商、邮件服务、合作伙伴的 API。

如果一个方框或一条箭头无助于回答其中任何一个问题,它多半应该放到另一张图上。

示例:一个有五个服务的在线商店

这家商店有 Web 和移动客户端、一个 API 网关和五个服务:

  • Catalog 从自己的 PostgreSQL 数据库提供商品数据。
  • Cart 把购物车保存在 Redis 中。
  • Orders 在自己的 PostgreSQL 数据库中创建订单,并向 Kafka 中的 orders 主题发布 OrderPlaced。
  • Payments 消费 OrderPlaced,通过 Stripe 扣款,保存结果,并向 payments 主题发布 PaymentSucceeded 或 PaymentFailed。
  • Notifications 消费 payments 主题,通过邮件服务商发送收据,或者发送支付失败的通知。

Orders 也消费 payments 主题,以便把订单标记为已支付。没有任何服务直接调用 Payments,结账流程是一条事件链。这个设计选择,正是图必须让人看见的:

在线商店的微服务图:客户端调用 API 网关,网关路由到 Catalog、Cart 和 Orders。Orders 发布 OrderPlaced;Payments 消费该事件,通过 Stripe 扣款并发布结果,Orders 和 Notifications 再消费这些结果。

15 个组件 · 15 个连接

在 Archboard 中打开
图表内容
  • Web and mobile clients
  • API gateway
  • Catalog
  • Cart
  • Orders
  • Catalog DB
  • Cart store
  • orders topic
  • Orders DB
  • Payments
  • Stripe
  • Payments DB
  • payments topic
  • Notifications
  • Email provider
  • HTTPS
  • GET /products
  • /cart
  • POST /orders
  • SQL
  • RESP
  • OrderPlaced
  • group: payments
  • PaymentSucceeded / PaymentFailed
  • group: orders
  • group: notifications
  • SMTP
本文中的在线商店:一个 API 网关、五个服务、四个各自只有一个所有者的数据存储;同步调用用实线箭头表示,Kafka 事件用经过具名主题的虚线箭头表示。

从左往右读:网关是唯一的入口,实线箭头是同步调用,虚线箭头是经过具名主题的事件,每个数据库都只挂在一个服务下面。

让微服务图保持可读的七条规则

1. 从入口画起

先在左侧画出客户端和 API 网关。它们确定了阅读方向,也表明到底有哪些服务对外暴露。从网关出发没有路径可达的服务就是内部服务,这一点值得让人看见。

2. 把每个数据库连到它的所有者

把每个数据存储放在拥有它的那个服务旁边,并且只连接这个服务。如果目前有两个服务共用一个数据库,也照实画出来。这是一个众所周知的坏味道,把它藏起来的图会让下一次迁移更难规划。

3. 区分同步调用和事件

使用两种线型,并在图例中说明:实线表示请求和响应(REST、gRPC),虚线表示经过消息代理的消息。把主题或队列画成节点。如果只画一个“事件总线”方框、把所有服务都连上去,就看不出谁生产了什么、谁消费了什么。

4. 给每条连线标上流经的内容

GET /products、POST /orders、OrderPlaced、PaymentFailed。标签把箭头变成契约,也是大家在处理故障时会在图上搜索的东西。

5. 按领域分组,而不是按技术

如果需要框,就按业务领域或负责团队给服务分组:购物、结账、客户沟通。“Node.js 服务”或“Java 服务”这样的框,回答的是一个没人会问的问题。

6. 把基础设施放到单独的视图里

Pod、sidecar、服务网格和仪表盘都很重要,但不该出现在逻辑视图里。把部署画在另一张图上,比如一张 Kubernetes 架构图,再把两张图互相链接起来。

7. 展示一条关键流程

挑出撑起营收的那条流程,这里是结账,让它容易跟读:在连线上给步骤编号,或者突出它的路径。一张图只放一条流程;第二条流程就单独复制一份视图来画。

系统大到一张图放不下时

超过二十个左右的方框,就该拆分了。一种行之有效的做法:

  • 一张全景图,以领域作为框,只画出领域之间最重要的连接。
  • 每个领域一张图,包含该领域的服务、数据存储和主题,并按上面的规则来画。
  • 流程图,只为少数几个关键旅程而画,比如结账或注册。

这与 C4 模型 能很好地对应:全景图接近系统上下文视图,每张领域图则是一张容器视图,其中服务及其数据库都是容器。

常见错误

  • 箭头没有标签。 读者只能猜,而且每个人猜得都不一样。
  • 网关直接连到数据库。 如果它真的读取数据库,那是一个需要讨论的设计决策,而不是画图时可以走的捷径。
  • 混用视图。 把 Pod、AWS 服务和业务能力放在同一张画布上,结果谁也看不懂其中任何一部分。
  • 隐藏消息代理。 Kafka 不是一根管道;主题有名称、所有者和消费者。
  • 一张从不更新的快照。 上一次架构重构时画的图只是一份历史文档。要么标上日期,要么让它保持实时更新。

在 Archboard 中构建

Archboard 为这张图的每个部分都提供了节点包,所以服务、存储和主题都是带端口的类型化节点,而不是长得都差不多的方框:

  1. 使用 Google 或 GitHub 登录,并新建一张图。
  2. 从基础节点包中添加一个 方框 表示客户端,再从云节点包中添加一个 API 网关。
  3. 用 C4 模型节点包中的 容器 节点添加各个服务,并在“技术”字段中填写语言或框架。
  4. 从数据节点包中添加 Postgres 和 Redis,放在各自的所有者旁边,再为 orders 和 payments 添加 主题 节点。
  5. 从 C4 模型节点包中添加 外部系统 节点,分别表示 Stripe 和邮件服务商。
  6. 连接端口。同步调用保持实线,把事件改为虚线,然后给每条连线加上标签。
  7. 把实时链接分享给团队;如果使用 Pro 方案,也可以把它嵌入你的文档。

上面的示例有 15 个图表节点,在 Free 方案的限制之内。相关示例可以打开 /examples/kubernetes-microservices 查看部署层面,打开 /examples/api-gateway-infographic 查看网关模式的信息图。

常见问题

一张图应该展示多少个服务? 以不用缩放就能看清为限,通常最多二十个左右的方框。超过这个数量,就按领域拆分,并在上层保留一张全景视图。

微服务应该用 C4 模型吗? 很合适。服务及其数据库在容器视图中都是容器。有些团队会把每个独立归属的服务建模为单独的软件系统;选定一种约定,并把它写下来。

Kubernetes、服务网格和监控该怎么表示? 画在单独的部署图上。逻辑视图回答谁和谁通信;部署视图回答它运行在哪里。

消费者是主动拉取的,那事件箭头是不是应该从消费者指向主题? 按数据流动的方向画:从生产者到主题,再到消费者。如果这一点对讨论有影响,再注明消费者是拉取模式。

怎样让图保持最新? 给图指定一个负责人,在包含改动的同一个 pull request 中更新它,并在文档中链接实时的图,而不是粘贴截图。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。