☰
diagram-design实战:让架构图变成可维护的代码资产
2026/10/19 12:27:28 网站建设 项目流程

做技术方案这些年,画架构图一直是个绕不开的活儿。前一阵帮团队整理系统说明文档,我发现自己最头疼的不是写多少字,而是画图:一张架构图用鼠标拖一晚上,改版本的时候又要重画一遍,评审会上改个模块名还得对着图调半天。后来我认真研究了一段时间 diagram-design,也就是用设计思维加代码化手段来做图表设计,终于把画图这件事从体力活变成了可控的工程流程。这篇文章把完整思路、工具选型、实操代码和踩坑记录都整理出来,适合正在搭文档体系、做架构设计,或者被拖拽画图折磨过的开发者参考。

1. 图表设计这件事,为什么值得单独拎出来说

1.1 先承认:大多数人对画图的认知停留在拖拽连线

我见过很多人画图,下意识就是打开画图工具,拖几个方框出来,连几条线,调一调颜色,完事。这种操作本身没问题,问题在于“图”在团队协作里的角色被低估了。架构图、流程图、时序图、状态图,本质上不是装饰品,而是信息传达的媒介,是系统设计的静态切片。

图表设计的核心不是“画得好看”,而是“画得准确、画得省心、画得能持续维护”。很多人画完一张架构图,过两个月系统迭代了,图就过期了,再也不想碰它。原因很简单:作图成本高,改图成本更高。一旦图变成一次性产物,它的价值就大打折扣。

1.2 diagram-design 到底解决的是什么问题

diagram-design 这个思路,核心就三件事:把图表当作代码来管理,把图表当作设计对象来推敲,把图表当作工程资产来沉淀。

先说“当作代码来管理”。如果用文本定义节点和连线,那么图表可以进版本库,可以 diff,可以走评审流程,可以被任何人拉下来重新生成。团队成员之间不用再通过微信传一份“最终版”图片,也不存在“你改了我没改”这种混乱状态。

再说“当作设计对象来推敲”。设计一张图和设计一个接口是一样的,要先明确读者是谁、要传达什么信息、信息层级怎么排、哪些细节可以省略。大部分人的架构图让人看不懂,不是因为图不清晰,而是因为设计者没想清楚这张图到底要回答什么问题。

最后是“当作工程资产来沉淀”。图一旦能够被代码生成,它就可以被固化在文档站点里,和版本说明一起发布。系统演进时,图跟着代码一起改。这才是 diagram-design 最值钱的地方。

2. 工具选型:拖拽派和代码派,我为什么最后选了代码

2.1 拖拽画图在真实工程里的三个痛点

我不是全盘否定拖拽工具,事实上很多场景下拖拽画图更高效,尤其是草图沟通阶段。但一旦进入正式文档、架构评审、交付物产出,拖拽工具的劣势就非常明显。

第一痛点是版本管理几乎为零。拖拽工具保存的文件往往是二进制或者专用格式,Git 里根本没法有效对比差异。你只知道这张图“改过了”,但改了哪里、为什么改,无从追溯。

第二痛点是协作容易互相覆盖。一个团队几个人同时维护一张图,后保存的人大概率会把前面的修改覆盖掉。没有合并机制,没有冲突提示,这也解释了为什么团队里的架构图总是“最后改的人说了算”。

第三痛点是模板风格不统一。每个人拖出来的图都有自己的审美偏好,有人喜欢圆角,有人喜欢直角,有人喜欢多配色。放在同一份文档里,风格五花八门,阅读体验很割裂。

2.2 代码画图的主要选项和我的选择

代码画图有不少成熟方案,我简单列一下:Graphviz 是最底层的,适合画有向图、依赖关系图,灵活度极高,但写起来比较原始;PlantUML 在时序图、用例图上很强,语法简单;Mermaid 文本轻量,适合 Markdown 文档里内嵌流程和时序;Diagrams 是专门面向架构图的 Python 库,用代码定义节点和连线,画出来的效果接近专业架构图模板。

我最后主力用的是 Diagrams,原因有三个。第一,它面向的场景和我的需求高度匹配,就是系统架构图、云架构图、部署拓扑图;第二,它内置了大量图表节点素材,不需要自己去网上找图标;第三,它基于 Python,我可以把节点封装成函数、把架构定义写成配置,批量生成不同视角的图。

2.3 不同场景的图表工具选型对照表

场景推荐方案理由
快速画个草图给同事看任意拖拽工具上手快,随手就能画
Markdown 文档里的简单流程图Mermaid文本内嵌,渲染方便
时序图、用例图PlantUML语法专注,表达力强
复杂依赖关系图Graphviz底层布局算法强大
系统架构图、云部署图Diagrams节点素材丰富,图文代码化
团队长期维护的架构资产Diagrams + 自动化脚本可版本化、可复用、风格统一

选型的关键不是“哪个工具最强”,而是“哪个工具最适合你的工作流”。如果你只是偶尔画一张图,拖拽工具完全够用;但如果你和团队需要长期维护一套架构视图,代码化几乎是绕不开的选择。

3. 核心实操:用 Diagrams 从零画一张架构图

3.1 环境准备:一个 Python 环境就够了

Diagrams 本身是一个 Python 库,底层依赖 Graphviz 做排版渲染,所以安装要分两步走。

第一步装 Graphviz 引擎。不同操作系统安装方式不一样,基于常见实践,Windows 上可以直接下载安装包并加入 PATH,macOS 上通过包管理器安装,Linux 发行版也有对应的系统包。安装完成后,建议在终端里跑一下dot -V确认版本能正常输出,这一步能提前暴露路径配置问题。

第二步装 Python 库。用pip install diagrams即可。如果当前 Python 环境里有多个项目,建议先建独立虚拟环境,避免包冲突。Diagrams 对 Python 版本有要求,实测下来 3.8 以上都稳定,新版本 3.10、3.11 也没有遇到兼容问题。

装完之后可以顺手验证一下导入:

python -c "from diagrams import Diagram; print('ok')"

如果输出了 ok,说明环境就绪。

3.2 最小可运行示例:先把水管接通

很多教程上来就扔一个几百行的架构图,读的人容易劝退。我的习惯是先跑通最小示例,确认环境没问题,再逐步增加复杂度。

from diagrams import Diagram, Edge from diagrams.generic.blank import Blank from diagrams.generic.network import Firewall from diagrams.generic.database import SQL with Diagram("最小示例:一次请求如何落库", show=False, direction="LR"): client = Blank("客户端") fw = Firewall("防火墙") db = SQL("数据库") client >> Edge(label="HTTPS") >> fw >> db

这段代码做了三件事:定义三个节点,用>>连出一条链路,最后导出为一张图。show=False表示生成后不自动打开图片预览;direction="LR"表示图从左往右布局。

运行python diagram.py,同目录下会生成最小示例:一次请求如何落库.png。第一次运行可能会花一些时间下载节点图标素材,后面再跑就会走本地缓存。

这里有必要解释一下>>这个操作符。在 Diagrams 里,a >> b表示 a 指向 b,也就是数据流或者调用方向。反过来,a << b表示 b 指向 a。这是理解和编写图表代码的关键,方向写反了,整张图的语义就变了。

3.3 完整案例:带集群、边标签和方向配置的架构图

最小示例只能验证链路,真实架构图一般要按层次组织节点,这时候就要用到两个重要概念:Cluster 和 Edge。

Cluster 用于图上区域分组,对应架构里的“接入层”“应用层”“数据层”这种逻辑边界。Edge 用于描述连接关系,可以加标签、颜色和线型。

来看一个更完整的例子。假设我们要画一个订单主链路的架构图,包含用户端、接入层、应用层、数据层四个部分:

from diagrams import Diagram, Edge, Cluster from diagrams.generic.blank import Blank from diagrams.generic.network import Firewall from diagrams.onprem.compute import Server from diagrams.generic.database import SQL with Diagram( "订单主链路架构", show=False, direction="LR", graph_attr={"nodesep": "0.6", "ranksep": "0.8"}, ): user = Blank("用户端") with Cluster("接入层"): waf = Firewall("WAF") lb = Server("负载均衡") user >> waf >> lb with Cluster("应用层"): order = Server("订单服务") inv = Server("库存服务") notify = Server("通知服务") lb >> order lb >> inv lb >> notify with Cluster("数据层"): order_db = SQL("订单库") cache = Blank("缓存") inv_db = SQL("库存库") order >> Edge(label="读写", color="blue") >> order_db order >> Edge(label="缓存", style="dashed", color="green") >> cache inv >> Edge(label="读写", color="blue") >> inv_db

这段代码看起来复杂,但拆开看其实就几件事:

  • direction="LR"让主流程从左向右展开,符合大多数人的阅读习惯;
  • Cluster把相关节点包进同一个虚线框,从视觉上强调层次边界;
  • Edge的style="dashed"表示异步或旁路调用,color用来区分读写链路;
  • graph_attr里的nodesep和ranksep控制同层节点间距和上下层间距,数值越大图越松散。

运行后,你会得到一张结构清晰的层次化架构图。这个图如果丢给拖拽工具,得手动框选、对齐、调颜色,至少半小时起步;用代码定义,改节点名、加服务、调整层次,只是修改几行文本的事。

3.4 导出格式和图片参数调优

Diagram 对象有几个常用参数值得讲清楚。outformat控制导出格式,支持 png、svg、jpg 等,我一般默认 png;如果需要矢量图放进文档,svg 更合适。filename可以自定义输出文件名,默认用的是标题。show控制生成后是否自动打开预览窗口,在服务器环境或批量生成时一定设为False。

图片清晰度方面,Diagrams 底层用 Graphviz 渲染,默认尺寸对屏幕阅读足够,但要放到打印材料或者高清 PPT 里,可以通过graph_attr里的dpi调整。比如graph_attr={"dpi": "200"}可以让输出图片更细腻。

还有一个小技巧:架构图节点文字默认使用的字体可能不支持中文,导致中文标签显示成方块。解决办法是给图指定系统里已有的中文字体,比如在 graph_attr 里设置fontname为中文字体名称。具体名称取决于你系统装了什么字体,Windows 常见的是 Microsoft YaHei,macOS 常见的是 PingFang SC。这个细节在交付中文文档时非常重要,我在实际项目中踩过好几次。

4. 设计方法论:让一张图从“能看”到“秒懂”

4.1 先定主流程,再补细节

好架构图的标准不是信息多,而是“读者扫一眼就能说出这张图在讲什么”。达到这个效果,第一原则就是先定主流程。

画图之前先问自己:这张图要回答什么问题?是“请求从哪里来、经过哪些服务、写到哪个库”,还是“系统分几个部署单元、每个单元内部有哪些组件”?两个问题对应两张图,不应该混在一起。

主流程确定后,用一条主链路串起来,其余内容作为分支挂上去。如果分支太多,优先保主干,把细节留给下一张子图。我在实际画图时给自己定过一个规则:一张架构图里,主链路上的节点不超过七个,超过就拆图。

4.2 颜色、命名、边风格都该有“语义”

很多人画图时调颜色纯粹为了好看,但颜色用多了反而增加认知负担。我推荐的做法是给颜色赋予固定语义,全网统一。

比如接入层统一用蓝色系,应用层用绿色系,数据层用橙色系,外部系统用灰色系。这样读者即使不看分组框,也能通过颜色判断组件所属层次。再比如连线:实线表示同步调用,虚线表示异步通知或事件流,红色高亮表示重点链路或故障链路,绿色表示成功路径。习惯一旦养成,团队里所有图都遵循同一套语言,看图效率会提升很多。

节点命名也很关键。命名不要写“模块A”“服务B”,而要体现职责,比如“订单服务”“库存服务”“消息推送”。如果某个节点同时承担多个职责,那就是拆分节点的信号,而不是在名字里堆名词。

4.3 信息密度控制:一张图只讲一件事

我在评审材料里见过很多“大而全”的架构图,把所有系统、所有依赖、所有边都画在一张图上,结果没有一处能看清。这种图的本质是设计者想用一张图证明所有工作量,而不是让读者理解系统。

信息密度控制的核心是“分层出图”。总体架构图只画到服务级,领域细节画到组件级,部署细节画到实例级。每一层图都聚焦一个抽象层级,读者按需钻取。

实操时可以按“业务视角”“系统视角”“部署视角”分别出图。业务视角面向产品和管理者,重在流程和边界;系统视角面向开发,重在模块间调用;部署视角面向运维,重在实例、区域和依赖。同一个系统,三张图,比一张大杂烩有价值得多。

4.4 图表评审检查清单

我画完图后会按这个清单过一遍,帮助很大:

检查项标准
主流程是否清晰读者 10 秒内能说出图的主线
是否有图例说明特殊颜色、线型的含义有标注
节点命名是否达意不使用“模块1”“服务A”
集群划分是否合理是否按逻辑边界而非代码模块
信息层级是否单一一图是否只聚焦一种视角
版本是否可控代码和图片是否同时提交到版本库
文字是否被人为截断标签是否被节点边框遮挡

这个清单是我在实际评审中总结出来的,基本可以覆盖大多数常见问题。

5. 常见问题与排查技巧实录

5.1 图标素材加载与缓存问题

Diagrams 的节点素材来自网络,第一次运行会触发下载,有时候会卡很久,甚至直接失败。实测下来,大部分“运行半天没反应”“生成出来全是默认图标”的问题都出在这一步。

我的处理方式是提前在网络正常的环境里把所有图标跑一遍,让本地缓存完整,然后拷贝到目标机器。Diagrams 支持环境变量指定图标库的存放目录,离线环境下可以复用这份缓存,避免每次重新下载。这个方法在无外网环境里画图特别管用。

5.2 中文字体显示成方块

生成图片里中文标签全部变方块,是放在文档里最尴尬的问题。原因就是 Graphviz 渲染时找不到合适的中文字体。

解决思路分两步。第一步,确认系统装了中文字体;第二步,在 graph_attr 里显式指定字体名称。注意字体名称不是文件名,是系统里注册的字体族名称,比如 Windows 下常见的“Microsoft YaHei”。设置后重新生成,中文基本都能正常显示。

5.3 布局混乱与节点重叠

图里节点挤成一团、边线交叉严重的时候,先别急着调代码,看看是不是布局方向没选对。从左到右还是从上到下,取决于主链路的数量。链路长就适合从上到下,分支多就适合从左到右。

如果方向调完还是挤,适当增大nodesep和ranksep。这两个参数一个管同层节点间距,一个管层级间距。数值太小是节点重叠,太大是图被拉得很空,实际使用时可以多试几个值。

5.4 方向写反、连线串位等新手高频错误

>>和<<的方向语义是新手上手最容易搞反的地方。记住一点:箭头总是从代码左侧的节点指向右侧的节点。a >> b画出来就是 a 到 b 的箭头。

另一个高频问题是集群里的边写在外面。如果一个边连接的两个节点都在某个 Cluster 内部,但 Edge 定义写在了 Cluster 外面,布局和语义都会出问题。写代码时尽量把集群内的连接关系放在集群代码块内维护,结构上更清晰,也不容易串位。

提示:Diagrams 生成图片时自身会做布局优化,不要试图通过大量隐形节点来强行控制位置,那是 Graphviz 时代的土办法,维护成本极高。

6. 几个让工作效率翻倍的进阶用法

6.1 用函数抽象公共节点,批量出图

架构图里的公共设施,比如防火墙、负载均衡、监控服务,在多个子系统里都会出现。与其每张图重复画一遍,不如封装成函数或工具模块。

def add_base_layer(lb): """接入层公共节点""" fw = Firewall("WAF") lb = Server("负载均衡") return fw, lb

这样维护一套基础组件,子系统出图时只需要描述自己特有的服务,整体风格也容易保持一致。批量出图时,配合脚本遍历配置项,一次产出全系统的架构视图,效率提升非常明显。

6.2 把架构图放进文档和评审流程

代码化之后,架构图可以和文档一起发布。团队写方案时,直接把生成图片的命令写进文档构建脚本,每次文档更新时图也会重新生成。不再需要“截图-粘贴-上传”这套动作,也不存在“图过期了但没人发现”的问题。

版本库里的 diff 能力也很有用。一次架构变更对应若干行节点和连线的增删,评审人看 diff 就能理解改动意图,比盯着两张图片比对高一截。

6.3 与自定义图标库结合,打造团队统一风格

Diagrams 支持 Custom 节点,可以加载本地图片作为节点图标。团队如果积累了统一的品牌图标库,完全可以通过 Custom 来替换默认素材。

from diagrams.custom import Custom order = Custom("订单服务", "icons/order.png")

这里要注意,Custom 节点接收的图片路径必须是本地路径,建议用相对路径并保持图标目录随代码一起纳入版本库,这样其他同事拉下来之后直接运行就能出图。用自定义图标的图放在对外文档里,整体辨识度和专业度会好很多。

我个人在实践里最深的一个体会是:画图这个事,从一开始就应该被当作代码工程的一部分来对待,而不是文档收尾时的附加工作。把图表设计前置、把节点和连线变成可维护的文本,带来的收益会在项目迭代到中期时发酵得特别明显。每次系统变更,顺手把图改了的习惯,会让团队的文档资产始终保持鲜活,不至于在某个关键时刻拿出一张早已过期的废图。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询