ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用文本定义图表:让架构图、流程图像代码一样可维护

用文本定义图表:让架构图、流程图像代码一样可维护 1. 一张图背后藏着的两种工作方式先聊一个我自己的经历。上个月做系统重构评审需要给团队和合作方讲清楚新的模块划分和调用关系。我打开画图工具拖了二十分钟框框和箭头结果还是乱成一团。后来换了个思路直接用 text 描述的方式把整张图写出来配合自动渲染三分钟就得到一张结构清晰的架构图。这个写图的思路就是本次项目要说的核心——diagram-design。很多人把画图理解成视觉劳动认为只要手够快、工具用得熟就行。但真正干过复杂项目的人会明白图表的核心价值从来不是好看而是把复杂关系说清楚。而说清楚这件事靠鼠标拖拽往往效率极低尤其是在图需要频繁修改的时候。这也是 diagram-design 这类图表即代码思路逐渐走红的原因它把一张图的构建过程从手工绘制变成了结构化描述。diagram-design 这个名字本身的含义就是在设计一张图之前先把它当成一个系统来拆解有哪些节点、节点之间什么关系、信息流向是什么、哪些元素属于同一层。这个拆解过程恰好和写代码的思维一致。所以在这篇内容里我会把我用这套思路做过的图、踩过的坑、沉淀下来的方法一起整理出来。适合的人群很明确需要频繁画架构图、流程图、时序图的开发者和产品同学以及所有觉得画图耗时、改图更耗时的人。如果你只是想找个工具随便画两笔那这篇内容对你帮助不大如果你想建立一套可持续迭代的图表设计方案那这篇内容正好是为你准备的。2. 工具选型代码生成型图表为什么越来越受欢迎diagram-design 的思路落地第一步是选对工具。市面上图表工具很多但大致分两类一类是所见即所得的拖拽式工具另一类是基于文本描述、由渲染引擎自动生成图表的工具。两种我都深度用过说下我的真实感受。2.1 拖拽式工具的上限与瓶颈拖拽式工具最有代表性的就是 draw.io 和各类在线白板。它们的优点很直观上手零门槛谁都会用。但它有几个让人头疼的问题第一改图成本高。假设一张架构图里有十个服务和五条调用链产品同学说把第三个服务拆成两个你需要在画布上新增框、重新连线、调整位置运气不好还要被自动吸附功能气到摔鼠标。第二规范难以统一。不同人画的图框的大小、颜色、线型、字号完全是随机的。十个人画同一个系统能出来十种截然不同的风格放在文档里显得特别乱。第三难以做版本管理。拖拽式工具保存的文件格式通常是二进制或者特定的 XML你用 git 根本没法 diff两个人同时改一张图合并冲突基本靠手动重画。这些瓶颈在个人小项目里不明显但一旦进入团队协作、文档持续迭代的场景就会被放大到非常难受的程度。2.2 代码生成型工具的复利效应代码生成型工具的代表是 Mermaid、PlantUML、Graphviz 这一挂。它们的核心理念是你用一套结构化语法描述图的内容渲染引擎负责把它变成视觉元素。位置计算、连线路由、布局排列这些脏活累活全部交给引擎处理。这样做带来的好处是复利式的修改成本极低。想加一个节点就加一行文本想改一条连线就改一个箭头定义。改完重新渲染图自己会调整布局。天然支持文本 diff。你可以把图定义文件放进 git 仓库每一次变更都有据可查。风格统一。同一个语法写出来的图渲染风格天然一致不需要人为去对齐框的大小和颜色。与文档系统集成方便。Markdown 里直接嵌代码块渲染出来就是图文档里外一致。有人会担心这不是把简单的事情变复杂了吗画个图还得记单词、学语法我的回答是对于一次性草稿图拖拽确实更快但对于要长期维护的图表代码型工具的前期学习成本会在第一次修改时全部赚回来。下面用表格对比一下我在实际项目中对几款工具的评价工具语法上手难度布局自动化的成熟度适合场景我最在意的短板Mermaid低高架构图、流程图、时序图、甘特图复杂图布局偶尔抽风PlantUML中高时序图、用例图UML 场景语法偏重渲染偏慢Graphviz中高极高基于图算法大规模节点关系图、树结构需要写 dot 语言风格老旧Excalidraw无语法手绘风不适用快速头脑风暴、低保真草图不适合做严肃架构文档我的建议是日常首选 Mermaid遇到复杂 UML 场景换 PlantUML搞大规模关系图谱再上 Graphviz。这套组合在绝大多数场景下都够用了。2.3 为什么把重点放在 Mermaid如果你时间有限只想学一个我强烈推荐 Mermaid。原因有三条第一它的语法是最接近自然语言的。比如画一个流程图你写 A--B它就知道 A 到 B 有一条带箭头的连线。这种直觉化设计非常友好。第二它的生态最活跃。GitHub 官方 README 原生支持 Mermaid 渲染很多开源项目的文档里都在用各大笔记平台、在线 Markdown 编辑器几乎都内置了支持。第三它足够轻量。本身是一个 JavaScript 库可以嵌入任意网页也可以命令行方式批量把图渲染成 SVG 或 PNG方便接入自动化流水线。所以我后面的实战内容都会基于 Mermaid 展开。理解了它的设计思路再去看其他同类工具会非常容易触类旁通。3. 从三个高频场景拆解图表的设计要点工具选好了具体到画图这件事不同场景的设计侧重点差异很大。我挑日常最高频的三类图架构图、流程图、时序图逐一拆解它们的设计要点和落地方式。3.1 架构图层次感比细节更重要架构图的核心目的是让人一眼看懂系统的分层结构和模块归属。我见过很多人画架构图恨不得把一个服务的几十个配置项全写在框里最后整张图密不透风谁看了都头疼。架构图的设计原则我总结成三条只画关键模块不画配置细节。框里面的内容应该是一个有业务含义的名字而不是技术参数。通过分组表达层次。把属于同一层或同一域的模块用子图容器圈起来视觉上自然形成边界。连线要少而精。每条线都应该有明确的语义调用、依赖、消息传递还是数据流。语义混用的线会让看图的人非常困惑。说得再具体一点一个好的架构图应该能回答三个问题系统分几层每层有哪些模块层与层之间怎么协作至于某个模块用了什么框架、什么数据库那是另一张图的事。3.2 流程图路径清晰大于分支完整流程图最容易犯的毛病是什么都想画进去结果条件分支牵出十几个分支每个分支下面还有子分支最后读者完全迷失在路径里。做流程图时我会强制自己遵循一个原则一条主线走到底分支只画关键路径。所谓关键路径就是最常见、对业务结果影响最大的那条链路。异常处理、边界条件、兜底逻辑如果确实重要应该用区块标注异常分支单独拆图而不是挤在同一张图里。另外流程图的节点命名也有讲究。动词开头的命名比如校验参数下发任务回调通知比名词命名比如参数校验模块任务下发回调更容易让人在脑子里形成顺序感。这一条是很多教程不会提的细节但实践下来效果非常明显。3.3 时序图生命线和消息顺序是灵魂时序图表达的是一组对象之间随着时间发生的交互。它的阅读逻辑是从上到下代表时间顺序。所以设计时序图的核心是保证消息的顺序和语义完全准确而不是追求图形的美观。时序图有几个常见的设计误区为了省事把所有消息都放在同一条生命线上导致时序关系完全看不出来。分不清同步、异步消息。在 Mermaid 里同步调用是实线箭头异步消息是虚线箭头。混用会让语义严重失真。只画正常流程不标注返回值和异常消息导致看图的人不知道每一步的产出是什么。我画时序图的经验是先列清楚整个交互过程的消息列表再动笔定义而不是边想边画。消息列表确定了图就是顺理成章的事。4. Mermaid 实战从零定义一张架构图工具选好了设计原则也有了接下来进入实操环节。我会从零开始完整走一遍用 Mermaid 定义一个系统架构图的过程把每一步的设计思考写清楚。4.1 定义节点命名、形状与含义先看一段最简单的 Mermaid 流程图定义graph TD A[前端应用] -- B[接入层网关] B -- C[订单服务] B -- D[用户服务] C -- E[(数据库)]graph TD表示这是一张从上到下Top-Down布局的流程图。每一行定义一个节点A[前端应用]表示创建一个名为前端应用的节点方括号表示矩形形状。--代表带箭头的实线连线。我平时画架构图时统一用方括号表示系统/服务用圆角矩形A(文本)表示逻辑模块用圆柱体A[(文本)]表示存储层。这样仅仅通过形状读者就能快速区分不同类型的元素这是成本最低的视觉分层手段。从设计角度讲节点命名建议用业务名称 技术后缀的组合方式比如把C[订单服务]而不是C[order-service-2.0]。后者的技术感太重对不熟悉代码细节的读者不友好。架构图是给人看的不是给机器看的。4.2 连线语义让每一条线都表意明确Mermaid 支持的连线类型很丰富但很多人只用到了--一种。这有点像写代码只会用 if 不会用 else表达能力天然受限。实际项目中我常用的连线类型就这几类--带箭头的实线表示同步调用或强依赖关系。---不带箭头的实线表示相邻或关联但没有强依赖。-.-带箭头的虚线表示异步消息或非关键路径的依赖。粗箭头表示关键路径上的核心流转通常一张图里最多出现两三次。我最近画一个订单系统的架构图时就同时用到了这四种连线核心的下单链路用粗箭头服务之间的同步调用用实线箭头消息队列的异步通知用虚线箭头数据表之间的关联用无箭头实线。整张图渲染出来之后团队评审时一眼就抓住了主流程与次要不重要的信息区分得清清楚楚。4.3 子图容器分组的正确姿势架构图最常见的痛点是如何表达层次结构。Mermaid 里用subgraph关键字定义子图子图内部可以继续放节点和连线。看一下实际例子graph TB subgraph 接入层 A[前端应用] B[接入网关] end subgraph 业务层 C[订单服务] D[用户服务] end subgraph 数据层 E[(业务数据库)] F[(缓存)] end A -- B B -- C B -- D C -- E D -- F渲染之后接入层、业务层、数据层会被三条带边框的容器包起来视觉上自然分成三块。这是架构图层次感的基础。但这里有一个非常关键的坑子图之间的连线定义位置会影响布局效果。在 Mermaid 中子图容器内部的边如果写成子图的外部有时会导致子图失去容器效果节点直接逃逸到最外层。我踩过这个坑之后养成了一个习惯先定义完所有子图和内部节点再理清跨层边的定义位置定义边时尽量放在所有 node 定义完之后。这样渲染基本不会出问题。4.4 样式定制把视觉噪音降到最低Mermaid 默认的样式是浅色背景、黑边框实用但没什么辨识度。如果图要放进正式的技术方案文档我会做一点轻量定制但原则是克制。常用的定制手段有三个第一用style给关键节点加底色。比如核心服务用浅蓝色背景存储层用浅灰色背景。第二用linkStyle调整连线的颜色和粗细重要路径加粗非关键路径用浅色。第三在定义节点时内联设置形状样式比如给异常节点加虚线边框。我见过有人把图渲染得五彩斑斓从大红到深绿全用上了结果视觉重点完全丢失。我自己的标准是整张图不超过三种强调色非强调元素一律黑白灰。图表设计的第一要务是信息传达颜色是辅助工具不是主角。5. 流程图与时序图的常见边角问题架构图是面的表达流程图是线的表达时序图是时间的表达。这三者各有各的坑我挑几个高频问题展开说。5.1 条件分支的表达与布局控制流程图中最常见的需求是条件判断。Mermaid 用{}定义判断节点。实际代码长这样graph LR A[收到请求] -- B{参数是否合法} B -- 是 -- C[进入业务处理] B -- 否 -- D[返回错误码]这里有两个容易踩的细节。第一个是连线标签的写法。B -- 是 -- C里的是是这条连线的文字说明渲染后会显示在线的旁边。很多人不知道这个语法会把判断结果写进节点文本里导致图特别啰嗦。第二个是分支的方向控制。Mermaid 在LR从左到右布局下默认分支朝左右展开在TD从上到下布局下分支朝上下展开。如果你发现分支方向不符合预期可以给分支边加x方向的定语比如B -- 否 -- D改成B -- 否 --|x| D可以强制横向。不过这个语法不同的 Mermaid 版本存在差异如果遇到布局问题优先考虑调整整张图的布局方向而不是跟引擎死磕。5.2 子流程封装与模块化真实的流程很少是单层的。一个订单处理流程里可能包含库存预占支付请求风控校验等多个子流程。如果在同一张图里平铺所有步骤图会变得非常长。Mermaid 没有原生支持子流程图不同版本支持程度不统一我的习惯是用子图把子流程封装起来。子图内部可以自己是一段完整流程外部只保留一个入口和一个出口。这样看图的人只需要关心主流程的推进想看细节再钻进子图。如果子图实在复杂更推荐的做法是单独画一张子流程图在父流程中用一个特殊形状的节点标注详见 XX 图。这种做法有点像代码里的函数拆分图与图之间保持职责单一整体文档才清晰。5.3 时序图的消息编号与参与者的边界时序图在评审中最大的争议点往往不是画了什么而是消息漏了没。我见过太多时序图几条线画完了事但实际交互至少有十几个来回。我的建议是两步走第一步写一个消息清单。参与角色之间的每一次交互按先后顺序列出来。这个清单可以直接写进文档里作为时序图的文字版补充。第二步在定义时序图时保持参与者的声明顺序与阅读顺序一致。Mermaid 时序图的参与者声明顺序就是渲染后的左右排列顺序。把最重要的参与者放在最左侧次要的放在右侧阅读体验会好非常多。时序图还有一个日常容易忽略的点返回值的表达。Mermaid 中同步消息返回可以用activate和deactivate加生命周期块返回值用虚线箭头。如果不加激活块图看起来就是一条直通到底的调用链看不出谁等谁、谁在什么时间点返回。6. 让图表像代码一样可维护图表设计做到这里已经能画出一张像样的图了。但真正的项目实战考验的不是画单张图的能力而是如何让一批图在几十个版本迭代中保持整洁、准确、可维护。这一章节我分享我在团队里落地 diagram-design 流程的一些心得。6.1 目录规范与命名约定我建议团队在文档仓库里划出一个固定目录比如docs/diagrams所有的图表定义文件统一放这里。文件名要有清晰的语义order-service-architecture.md、payment-flow.md、user-login-sequence.md。这样任何人搜索时都能快速定位到对应的图。更重要的约定是图的标题、节点命名、连线标签统一使用项目团队熟悉的名词表。很多人觉得命名是小事但在项目持续半年以上时命名不一致会让图的维护成本翻倍。举个例子有人叫用户服务、有人叫User Service、有人叫user-center三张图放一起你不知道它们是不是同一个东西。我强烈建议在图文件头部用注释写明这张图的更新时间和核心名词表哪怕只有一行也能让后来的维护者少踩很多坑。6.2 图表定义纳入版本管理文本定义的图有一个天然优势它天然可以纳入 Git 管理。这意味着你可以查看每一张图的历史变更知道它是什么时候、因为什么原因被改动的。这一能力在团队协作中的价值怎么强调都不过分。实操上我习惯一个做法在 CI 流程里加一步自动渲染检查。程序自动读取所有.md文件中的 Mermaid 代码块尝试渲染如果语法错误就报错。这样能确保图定义文件的语法永远是可用的不会出现文档里放了一段残图代码半天没人发现的情况。渲染出来的 SVG 或 PNG 也可以自动生成省去每次手动导出的麻烦。6.3 把图的说明文字和图的定义放在一起很多人画完图就丢一个图文件旁边没有任何说明文字。这种做法的问题在于图只表达了关系很难表达设计背景和取舍原因。比如一张架构图里为什么订单服务直连数据库而不是通过数据访问层如果不说明后来的人很可能在没有上下文的情况下优化掉这个决定结果搞出性能问题。我个人的习惯是每张图文件里用一段文字简述这张图的背景、适用范围和关键取舍。不需要长篇大论三五句话就够。但这段文字的重要性和图表本身相当。7. 我实际踩过的四个图设计坑经验都是踩坑踩出来的。这一节分享我在 diagram-design 实践中印象最深的四个坑每一个都消耗过我的时间。7.1 过度追求全导致图完全没法看早期画系统架构图我恨不得把十几个服务、几十条依赖全塞进一张图里。结果就是节点密密麻麻、连线交叉乱飞渲染出来我自己都不敢认。后来觉悟过来一张图的原则应该是一个主题、一张图、只画核心信息。信息量大不是靠硬塞进一张图解决的而是靠拆分成多张图、分别表达不同主题来消化。7.2 节点命名使用内部技术编号有段时间我画图喜欢直接用服务名或表名的简称觉得这样内部同学看得懂。后来发现只要图流转到合作部门或上层评审这些简称就成了阅读障碍。从此我给自己立了一条规矩图中出现的所有名字必须是业务文档里能查到的正式名称。技术内部名称只写在节点的 title 属性或说明文字里。7.3 图标和形状的滥用有些工具的节点形状特别多有的图表甚至给每个节点都换一种形状。形状确实能表达语义但语义的区分度是有限的。人的视觉能清晰区分的形状大概就那么三四类矩形、圆角矩形、圆柱、菱形。超过这个数量看图就需要反复对照图例反而增加负担。我现在只保留四类形状而且每一类的语义在团队内部有明确规定。7.4 忽略渲染目标终端的适配同一张图放在宽屏显示器上和在手机上阅读体验差异很大。用 Mermaid 生成宽图后在手机上要横向滚动才能看全。我的办法是文档系统里尽量用 SVG 格式这样缩放不失真如果是分享到 IM 工具再准备一份适当调大节点间距的 PNG 版本避免关键路径粘成一团。8. 一张一图推而广之的扩展路径diagram-design 这套思路的价值不仅限于画软件架构图。它的底层逻辑是先把信息结构化再谈视觉呈现这个思想可以迁移到很多场景。我在日常工作中还会用同样的思路做这几类事情用流程图梳理业务操作手册把复杂的线下流程变成可执行的标准路径。用时序图画跨系统对接方案每次和外部团队联调之前先出一张时序图能减少大量口头沟通的误差。用架构图整理系统文档目录让新同学快速理解一个系统的整体边界。这些场景的共同点是信息本身是结构化的只是缺一个合适的载体。diagram-design 就是在帮你找到这个载体。如果你看完这篇内容想落地我建议从一个小场景开始挑一张你最近画得最痛苦的系统图用文本定义的方式重新表达一遍。你不需要一次性掌握所有语法只需要会定义节点、连线和子图就已经能覆盖大多数场景了。等这张图跑通再逐步尝试更复杂的布局和样式定制。我个人的体会是图表设计这件事真正值钱的部分不是画面的美观程度而是你脑子里的信息结构是否清晰。工具和语法只是把这份结构表达出来的手段。当你开始用结构的眼光看待图表时会发现画图和写代码之间的边界其实比想象中要模糊得多。
返回列表