☰
diagram-design:模型驱动架构图设计,让系统图表可维护可追溯
2026/10/15 8:27:16 网站建设 项目流程

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题

第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么画图——选个模板、拖几个框、连几条线,导出 PNG 就完事了。但真正在项目里画过架构图、流程图、时序图的人都知道,画出一张“能看”的图不难,难的是画出一张“能活过三个月”的图。三个月后需求变了、模块拆了、接口改了,你回头再看那张图,发现它跟代码已经完全对不上,于是要么硬着头皮改,要么干脆删掉重画。diagram-design 要解决的,正是这个“图的生命周期”问题。

我把它理解成一套以“可维护性”为第一目标的图表设计方法论加工具链。它不只是关于“怎么画”,更是关于“怎么让图跟系统一起演进”。核心关键词包括:图表即代码、语义化建模、分层抽象、自动化渲染、版本可追溯。这套东西适合谁?如果你是后端开发、架构师、技术文档工程师,或者任何需要长期维护系统设计图的人,那它跟你直接相关;如果你只是偶尔画个流程图发群里,那可能用不上这么重的方案,但里面的分层思路依然值得借鉴。

先说说为什么传统的“拖拽式画图”在工程项目里会翻车。拖拽工具(比如各种在线白板、桌面绘图软件)的本质是“像素编辑”,你画的是一个个图形对象,它们之间没有语义关系。你画了一条从 A 到 B 的箭头,工具只知道“这里有一条线”,不知道“A 调用 B”。所以当 A 改名了,你得手动去改图上的文字;当 B 被拆成 B1 和 B2,你得手动加框加线。图越大,维护成本越高,最后高到没人愿意碰。diagram-design 的思路是把图变成“代码的投影”——你先用结构化文本描述系统,再由渲染器生成图。这样改的是描述,图自动跟着变。

这个思路不是新东西,PlantUML、Mermaid、Graphviz 都在做类似的事。但 diagram-design 的差异点在于它强调“设计”而不只是“画”。它把图表分成几个层次:概念层、逻辑层、物理层。概念层画的是业务实体和它们的关系,逻辑层画的是服务、模块、接口,物理层画的是部署节点、网络、存储。每一层有自己的抽象级别,层与层之间有映射规则。这样做的好处是,当物理部署变了(比如从单机变集群),你只需要改物理层的描述,逻辑层和概念层的图不受影响。这种分层思想在大型系统里特别值钱,因为不同角色关心不同层——产品经理看概念层,开发看逻辑层,运维看物理层。

还有一个容易被忽略的点:diagram-design 强调图的“单一事实来源”。什么意思?就是同一个系统信息,不应该在多个地方重复维护。比如“订单服务依赖支付服务”这个事实,应该只在一个地方定义,然后架构图、时序图、部署图都从这个定义里派生。传统做法是画架构图时写一遍,画时序图时又写一遍,两边一旦不一致就出问题。diagram-design 通过统一的模型描述文件来解决这个,所有图都是这个模型的“视图”。这有点像数据库里的“范式化”——消除冗余,保证一致性。

我刚开始接触这套方法时,觉得它太重了:画个图还要写模型文件?但后来在一个中型项目里试了一次,发现真香。那个项目有十几个微服务,接口天天变,用传统方式画的图两周就废了。换成模型驱动后,改一个服务名只需要改一行描述,所有相关图自动更新。而且因为模型文件进了 Git,每次变更都有记录,谁在什么时候改了哪个依赖关系,一清二楚。这种可追溯性在排查问题时特别有用——你可以对着某个版本的图去理解当时的系统状态。

所以 diagram-design 的本质,是把“画图”这件事从“美术工作”变成“工程工作”。它要求你像写代码一样写图:有结构、有抽象、有版本、有自动化。代价是前期学习成本,收益是长期可维护性。下面我会从设计思路、核心细节、实操过程、常见问题几个方面,把这套方法拆开讲清楚,尽量让你看完就能在自己的项目里试起来。

2. 核心设计思路拆解:为什么是“模型驱动”而不是“画布驱动”

2.1 画布驱动的三个致命伤

在深入 diagram-design 之前,先看清楚传统画布驱动的问题,这样你才能理解为什么值得换一套方法。画布驱动,就是你在一个无限大的白板上拖拽图形、调整位置、连线、加文字。它直观、上手快,但用在工程项目里有三个绕不过去的坑。

第一个坑是信息与表现耦合。在画布上,“订单服务”这个信息是以“一个圆角矩形加一段文字”的形式存在的。你想改文字,得选中那个矩形;你想改颜色,也得选中它。信息和它的视觉表现绑死了。这意味着任何信息变更都要通过视觉编辑来完成,而视觉编辑是手工的、易错的、不可批量化的。十个服务改名,你得改十次,每次都可能漏。

第二个坑是关系不可查询。画布上的箭头只是线条,工具不知道它代表什么。你没法问工具“哪些服务依赖支付服务”,因为工具只看到线条的坐标。这在排查影响面时很要命——你想知道改支付服务会影响谁,只能人眼看图,图大了就看漏。模型驱动则不同,依赖关系是结构化数据,一个查询就能列出所有上游。

第三个坑是版本对比困难。画布文件通常是二进制或复杂 XML,Git diff 出来是一堆坐标变化,根本看不出“这次改了什么语义”。而模型文件是纯文本,diff 出来就是“删了一行依赖、加了一个服务”,一目了然。这在代码评审时特别有价值——架构变更可以像代码变更一样被 review。

2.2 模型驱动的核心:把“图”降级为“视图”

diagram-design 的核心转变,是把图从“主体”降级为“视图”。主体是模型,图只是模型的一种呈现方式。同一个模型可以渲染成架构图、时序图、部署图、甚至表格。这就像数据库里的表是主体,报表只是视图——你可以从同一张表生成不同维度的报表。

模型里存什么?存实体、关系、属性。实体可以是服务、模块、数据库、队列、外部系统;关系可以是调用、依赖、发布订阅、数据流;属性可以是技术栈、负责人、SLA 等级。这些信息用结构化文本描述,比如 YAML 或自定义 DSL。然后渲染器读取模型,根据不同的布局算法和样式规则,生成不同视角的图。

这样做的好处是一次定义,多处使用。你定义“订单服务通过 HTTP 调用支付服务”,这个事实可以出现在架构图里(一条带标签的箭头),可以出现在时序图里(一次同步调用),可以出现在依赖矩阵里(一个勾),还可以出现在影响分析报告里(改支付服务影响订单服务)。如果每个图都手工画,这些信息要重复维护四遍,一致性全靠人肉保证。

2.3 分层抽象:让不同角色看不同的图

diagram-design 另一个关键设计是分层。它把系统描述分成三层,每层有不同的粒度和关注点。

概念层关注业务能力和业务实体。比如“订单”“支付”“库存”这些业务概念,以及它们之间的业务关系(下单要支付、支付要扣库存)。这一层不关心技术实现,产品经理和业务方看这一层。逻辑层关注服务、模块、接口。比如“订单服务”“支付服务”“库存服务”,以及它们之间的调用关系。开发看这一层。物理层关注部署单元、节点、网络、存储。比如“订单服务部署在三个容器里,连同一个数据库”。运维看这一层。

层与层之间有映射。概念层的“订单”映射到逻辑层的“订单服务”,逻辑层的“订单服务”映射到物理层的“订单容器组”。当物理层变化(比如容器从三个变五个),逻辑层和概念层不受影响。当逻辑层变化(比如订单服务拆成订单服务和订单查询服务),概念层可能不变(业务上还是“订单”这个能力),但物理层要跟着调整。这种分层让变更的影响范围可控,不会一改全改。

我自己的经验是,中小项目可以只做逻辑层和物理层,概念层看情况。但一旦系统上了规模,概念层就很有必要,因为它是对齐业务和技术的关键。很多团队技术和业务脱节,就是因为缺少这一层——开发画的技术图业务看不懂,业务画的业务图开发觉得太虚。概念层是两者的翻译层。

2.4 工具选型:为什么不用现成的画图工具

有人会问:我用 PlantUML 或 Mermaid 不也是文本驱动吗?为什么还要搞一套 diagram-design?区别在于,PlantUML 和 Mermaid 是“图描述语言”,你写的是“画一张时序图”的指令,图本身还是主体。diagram-design 是“模型描述语言”,你写的是系统模型,图是派生出来的。前者你还是在“画图”,只是用文本画;后者你是在“建模”,图只是模型的一个输出。

另一个区别是布局控制。PlantUML 的布局是自动的,但控制力弱,复杂图容易乱。diagram-design 通常允许你指定布局策略(分层、力导向、网格),甚至允许手工微调位置,但位置信息存在单独的布局文件里,不污染模型。这样模型保持纯净,布局可以按需调整。

工具链上,diagram-design 通常包括:模型解析器(读 YAML/DSL)、渲染引擎(生成 SVG/PNG)、布局引擎(计算坐标)、样式主题(控制视觉)。有些实现会集成到 CI 里,每次提交模型文件就自动重新生成图并发布到文档站点。这样文档永远是最新的,不会出现“图还是半年前的”这种情况。

3. 核心细节解析与实操要点:从零搭一套可用的图表系统

3.1 模型文件的结构设计

模型文件是整套系统的核心,它的结构设计直接决定后续好不好用。我推荐用 YAML,因为可读性好、工具支持多、diff 友好。一个基本的模型文件包含几个部分:entities(实体)、relations(关系)、layers(分层定义)、views(视图定义)。

实体部分,每个实体有id、name、type、layer、attributes。id是唯一标识,用于关系引用;name是显示名;type是实体类型(service、database、queue 等);layer标明属于哪一层;attributes放扩展信息。关系部分,每条关系有from、to、type、label。type可以是calls、depends_on、publishes、subscribes等。

这里有个关键决策:关系是单向还是双向。我建议只定义单向,方向代表依赖方向。比如“订单服务调用支付服务”,方向是从订单到支付。反向关系(支付被订单调用)可以通过查询推导,不需要单独定义。这样模型更简洁,也避免双向定义不一致的问题。

另一个决策是属性放多少。属性太多,模型文件臃肿,维护累;属性太少,图的信息量不够。我的经验是,只放“会影响图的内容或结构”的属性。比如技术栈会影响图标的颜色,那就放;负责人只影响文档表格,可以放但不必进图。SLA 等级如果要用不同线型表示,就放;否则可以不放。原则是:属性要么影响渲染,要么影响查询,否则不进模型。

3.2 分层映射的维护技巧

分层映射是容易出问题的地方。概念层的“订单”和逻辑层的“订单服务”之间的映射,如果手工维护,很容易漏或者错。我的做法是用命名约定加显式映射。命名约定是:逻辑层实体 id 以概念层实体 id 为前缀,比如概念层order,逻辑层order-service、order-query-service。这样大部分映射可以自动推导。对于不遵循约定的,再用显式映射表补充。

映射关系要定期校验。我写过一个简单的校验脚本,检查每个概念层实体是否至少映射到一个逻辑层实体,每个逻辑层实体是否映射到恰好一个概念层实体。如果发现孤儿实体(没有映射的),就报警。这个脚本进 CI,每次提交都跑,能提前发现建模错误。

还有一个技巧是用颜色区分层。概念层用蓝色系,逻辑层用绿色系,物理层用灰色系。这样一张图里如果混了不同层的实体,一眼就能看出来。当然,正常情况下不应该混层——一张图只画一层。但有时候为了说明跨层关系,会画混合图,这时候颜色就很有用。

3.3 渲染管线的搭建

渲染管线负责把模型变成图。基本流程是:解析模型文件 → 构建图结构 → 应用布局算法 → 生成 SVG → 应用样式主题 → 输出。每一步都有可配置项。

解析阶段,要注意引用完整性校验。关系里引用的实体 id 必须存在,否则报错。这个校验要在解析后立即做,不要等到渲染时才报错,那样错误信息不清晰。构建图结构时,要决定是否包含孤立节点。孤立节点是没有关系的实体,画出来可能显得乱,但删掉又可能漏信息。我的做法是默认包含,但提供选项过滤。

布局算法选择很关键。分层布局(layered)适合有明确方向的依赖图,比如调用关系;力导向布局(force-directed)适合无方向的关联图,比如服务拓扑;网格布局适合矩阵式的对比图。diagram-design 通常允许在视图定义里指定布局策略。我一般对架构图用分层布局,对部署图用网格布局。

样式主题控制视觉。我建议把样式和模型分离,模型里只放语义信息(比如 type 是 database),样式文件里定义 database 长什么样(什么颜色、什么图标)。这样换主题不用改模型。样式文件也进 Git,团队统一。

3.4 与 CI/CD 的集成

图表要活,必须自动化。手工生成图,迟早会忘。我的做法是把渲染命令写进 CI 配置,每次模型文件变更就自动重新生成图,并发布到文档站点。具体步骤:CI 检测到model/目录有变更 → 安装渲染工具 → 执行渲染命令 → 把生成的 SVG 复制到文档目录 → 提交回仓库或发布到静态站点。

这里有个细节:生成的图要不要进 Git。我建议进,因为这样文档站点可以直接引用,不需要在构建时再生成。但要在提交信息里标明是自动生成的,避免有人手工改。另一个细节是渲染失败怎么办。如果模型有错导致渲染失败,CI 应该失败并阻止合并。这样保证主分支的模型永远是合法的。

我还加了一个步骤:生成变更摘要。每次渲染后,对比新旧模型,生成一个“本次变更影响了哪些图”的报告,附在 PR 里。这样评审的人能快速知道这次改动的影响面。这个摘要可以用简单的 diff 工具实现,不需要太复杂。

4. 实操过程与核心环节实现:一个中型项目的完整落地记录

4.1 项目背景与初始状态

假设我们有一个模拟项目,叫“某跨平台订单系统”,包含十几个服务:订单服务、支付服务、库存服务、用户服务、通知服务、日志服务、配置服务、网关、几个数据库、几个消息队列。团队之前用在线白板画架构图,画了五六张,分散在不同人的账号里,版本混乱。新来的同事想看最新架构,得问三个人才能拼出全貌。

我们决定用 diagram-design 重构。第一步是盘点现有信息:把白板上的图、代码里的服务定义、部署配置里的节点信息收集起来,整理成一份实体清单。这一步花了大概两天,因为信息散落各处,有些服务连负责人都说不清依赖谁。但这一步很值,因为盘点过程本身就发现了几个“幽灵依赖”——代码里有调用但没人知道的。

4.2 模型文件的编写过程

实体清单整理好后,开始写模型文件。我们按层写:先写逻辑层,因为开发最熟悉。每个服务一个实体,id 用 kebab-case,比如order-service。type 用枚举值:service、database、queue、gateway、external。attributes 里放技术栈和负责人。

关系部分,我们从代码里的调用关系提取。具体做法是:用静态分析工具扫描代码,找出服务间的 HTTP 调用和消息收发,导出成 CSV,再转换成 YAML 关系。这一步自动化程度高,但要注意过滤掉测试代码和注释掉的调用,否则会引入假依赖。我们第一版就混进了几个测试用的 mock 调用,后来加了过滤规则才干净。

写完后跑校验脚本,发现几个问题:有两个服务 id 重复(不同人用了不同命名),有三个关系引用了不存在的实体(服务改名了但关系没改)。修完这些,模型文件大概 300 行 YAML,覆盖了所有服务和主要依赖。

4.3 视图定义与渲染

模型有了,接下来定义视图。我们定义了四个视图:全局架构图(所有服务加主要依赖)、订单域架构图(只含订单相关服务)、部署图(物理节点和容器)、依赖矩阵(服务间的依赖表格)。每个视图在 YAML 里定义包含哪些实体、用什么布局、什么样式。

全局架构图用分层布局,按调用方向分层:网关在最上,业务服务在中间,数据存储在下面。订单域架构图类似,但只含子集。部署图用网格布局,按机房和节点分组。依赖矩阵用表格渲染,行和列都是服务,交叉点标记依赖类型。

渲染命令大概是diagram-render --model model.yaml --view architecture --output out/architecture.svg。第一次渲染出来的图有点乱,主要是布局参数没调好。我们调了节点间距、层级间距、边路由策略,试了七八次才满意。这里有个经验:布局调优要趁早,因为后面实体多了再调更麻烦。我们后来加服务时,因为布局参数已经稳定,新图基本不用调。

4.4 集成到文档与 CI

图渲染好后,集成到文档站点。我们用静态站点生成器,把 SVG 嵌入 Markdown 页面。每个视图一个页面,页面里除了图,还有从模型自动生成的实体表格(列出所有服务、技术栈、负责人)。这样文档既有图又有表,信息完整。

CI 集成方面,我们在仓库里建了.ci/render-diagrams.sh,内容就是安装工具、跑渲染、复制输出。CI 配置里加一个 job,监听model/和views/目录的变更,触发渲染。渲染成功后,把生成的 SVG 和实体表格提交回仓库的docs/目录。提交信息自动生成,包含变更摘要。

这里踩过一个坑:CI 环境的字体问题。渲染 SVG 时如果字体缺失,文字会变成方块。我们在 CI 镜像里预装了需要的字体,并在渲染配置里指定字体族。另一个坑是渲染时间。模型大了以后渲染变慢,我们加了缓存,模型没变的视图跳过渲染。这样大部分提交只渲染受影响的视图,速度快很多。

4.5 日常维护与变更流程

系统跑起来后,日常维护流程是:开发改代码 → 如果依赖关系变了,同步改模型文件 → 提交 → CI 渲染 → 文档更新。关键是要让改模型成为开发流程的一部分,而不是额外负担。我们的做法是把模型文件放在代码仓库里,跟代码一起 review。改依赖关系的 PR 必须同时改模型,否则 CI 会报“模型与代码不一致”(我们写了个脚本对比代码调用和模型关系)。

这个一致性检查脚本是整套系统的点睛之笔。它扫描代码提取实际调用关系,跟模型文件对比,发现不一致就报警。这样模型不会跟代码脱节。当然,脚本不可能 100% 准确(动态调用、反射等识别不了),但能覆盖 80% 的常见情况,剩下的靠人工 review。

变更流程上,我们规定:新增服务必须先在模型里定义,再写代码;删除服务必须先从模型里删,再删代码。这样模型始终是“设计先行”的。一开始有人觉得麻烦,但用了两个月后,大家发现查依赖、评估影响面比以前快多了,也就接受了。

5. 常见问题与排查技巧实录:那些文档里不会写的坑

5.1 模型与代码不一致怎么办

这是最常见的问题。表现是:代码里明明调用了某个服务,但模型里没有这条关系;或者模型里有,代码里已经删了。原因通常是开发改了代码忘了改模型,或者改模型时手误。

排查思路:先跑一致性检查脚本,它会列出所有不一致的条目。然后逐条确认:是代码对还是模型对。如果是代码对,改模型;如果是模型对,改代码。这里要注意不要盲目相信脚本,因为脚本可能误报(比如把注释里的调用当成真调用)。我的经验是,脚本报的每一条都要人工确认,确认后要么改代码要么改模型,不要留着“已知不一致”。

预防措施:把一致性检查放进 CI,不一致就阻止合并。这样问题在合并前就暴露,不会积累。另外,模型文件里可以加last_verified字段,记录最后一次人工确认的时间,超过三个月就提醒重新确认。

5.2 图太乱看不清怎么调

图乱了通常是三个原因:实体太多、关系太密、布局参数不合适。对应三种解法。

实体太多,就拆视图。一张图不要超过 20 个实体,超过就按域拆。比如全局图拆成订单域、支付域、用户域。每个域一张图,域间关系用“外部依赖”节点表示,不展开细节。

关系太密,就过滤关系。只画主要依赖,次要依赖(比如日志、监控)用虚线或省略。或者按关系类型分层显示,比如只显示同步调用,异步消息用开关控制。diagram-design 通常支持在视图定义里过滤关系。

布局参数不合适,就调参。常见的参数有:节点间距(太小人挤人,太大图太空)、层级间距(影响纵向长度)、边路由(直线、曲线、正交)。我的经验是,分层布局的层级间距设为节点高度的 1.5 到 2 倍比较舒服;边路由用正交线在架构图里最清晰,用曲线在拓扑图里更自然。

5.3 渲染性能问题

模型大了以后渲染变慢,尤其是力导向布局,复杂度可能是 O(n²) 甚至更高。优化思路:增量渲染,只渲染变更的视图;缓存布局,布局结果存下来,下次直接读;简化模型,渲染时只加载当前视图需要的实体和关系,不是全量加载。

我们项目里,全量渲染要 30 秒,增量渲染降到 3 秒以内。具体做法是:给每个视图算一个哈希(基于它包含的实体和关系的哈希),哈希没变就跳过渲染,直接用上次的输出。这个逻辑写在渲染脚本里,CI 调用时自动生效。

另一个性能坑是SVG 文件太大。实体多了以后 SVG 可能几 MB,浏览器打开卡。解法是简化 SVG,去掉不必要的元数据、合并重复样式、用符号引用代替重复图形。有些渲染器支持--optimize选项,开启后文件能小一半。

5.4 团队协作中的命名冲突

多人维护模型时,命名冲突很常见。A 用了order-service,B 用了order_service,C 用了OrderService。渲染出来三个不同的实体,其实是同一个。

解法是制定命名规范并强制校验。规范包括:id 用小写 kebab-case,name 用 Title Case,type 用固定枚举。校验脚本检查所有 id 是否符合规范,不符合就报错。另外,可以维护一个“别名表”,把历史遗留的命名映射到标准命名,渲染时自动转换。这样老模型不用大改,新模型用标准命名。

还有一个技巧是用命名空间。如果系统很大,可以按域分命名空间,比如order:service、payment:service。这样不同域的命名不会冲突。但命名空间会增加复杂度,中小项目不必用。

5.5 常见问题速查表

问题现象可能原因排查方法解决方案
图里实体缺失视图定义没包含该实体检查视图的 include 列表把实体加进视图
关系线指向错误关系 from/to 写反检查关系定义修正方向
文字显示为方块字体缺失检查渲染环境字体安装字体或指定字体族
渲染报引用错误关系引用了不存在的实体跑校验脚本修正实体 id 或删除关系
图太乱实体或关系太多数实体和关系数量拆视图或过滤关系
渲染慢模型大或布局复杂计时各阶段增量渲染或换布局
模型与代码不一致改代码没改模型跑一致性检查同步修改
命名冲突多人命名不统一检查 id 列表制定规范并校验

6. 工具链选型与扩展思路:不一定要从零造轮子

6.1 现成工具的组合使用

diagram-design 不一定非要自己写渲染器。可以用现成工具组合:用 YAML 存模型,用脚本把 YAML 转成 Graphviz DOT 或 Mermaid 语法,再用这些工具渲染。这样省去写渲染器的功夫,代价是布局控制弱一些,但中小项目够用。

具体做法:写一个转换脚本,读 YAML 模型,输出 DOT 文件。DOT 里定义节点和边,用 Graphviz 渲染成 SVG。样式通过 DOT 的属性控制。视图定义可以映射成不同的 DOT 子图。这个方案的好处是 Graphviz 成熟稳定,布局算法多,社区资源丰富。坏处是 DOT 语法有点老,复杂样式不好表达。

另一个组合是用 Mermaid。Mermaid 语法简单,适合快速出图,但布局控制更弱,复杂图容易乱。适合小项目或临时图。我的建议是:如果图简单(少于 15 个实体),用 Mermaid;如果图复杂,用 Graphviz;如果需要精细控制,自己写渲染器或用专业工具。

6.2 自定义渲染器的关键模块

如果决定自己写渲染器,核心模块有四个:解析器、图构建器、布局引擎、渲染器。解析器读 YAML 输出模型对象;图构建器把模型转成图结构(节点、边、属性);布局引擎计算坐标;渲染器生成 SVG。

布局引擎是最难的部分。如果不想自己实现,可以调用现成库,比如 D3.js 的布局模块、ELK 布局引擎、Graphviz 的布局。这些库通常有 Python 或 JavaScript 绑定,集成不难。渲染器相对简单,用 SVG 库画矩形、文字、路径即可。样式用 CSS 或内联属性控制。

我的经验是,自定义渲染器的价值在于精细控制和深度集成。比如你想在图上叠加实时监控数据(节点颜色反映健康状态),现成工具很难做,自定义渲染器就很容易。但如果你只是画静态架构图,没必要自己写,用现成工具组合更快。

6.3 与文档系统的集成

图最终要给人看,所以跟文档系统的集成很重要。常见集成方式有三种:嵌入 SVG、嵌入图片、动态渲染。嵌入 SVG 最清晰,支持交互(比如 hover 显示详情),但文件大。嵌入图片简单,但不支持交互。动态渲染是文档页面加载时调渲染接口,实时生成图,适合数据频繁变化的场景。

我一般用嵌入 SVG,因为清晰度和交互性最好。具体做法是渲染时生成 SVG,提交到文档仓库,文档页面用<img>或<object>引用。如果文档系统支持,可以用<object>以便支持 SVG 内的交互。另外,可以在 SVG 里加链接,点击节点跳到对应的代码仓库或文档页面,这样图就成了导航入口。

6.4 扩展方向:从静态图到动态视图

diagram-design 的模型驱动架构,天然适合扩展成动态视图。比如:叠加监控数据,节点颜色反映服务健康状态;叠加变更信息,最近变更的服务高亮;叠加流量数据,边的粗细反映调用量。这些扩展只需要在渲染时读额外的数据源,合并进模型,再渲染。

我们项目里做过一个简单扩展:渲染时读 Git 日志,把最近一周有变更的服务标黄。这样看架构图时,一眼就知道哪些服务最近动过,评估影响面时更有针对性。实现不难,就是渲染脚本里加一步:读 Git 日志,匹配服务名,改节点样式。

另一个扩展方向是交互式探索。生成的 SVG 支持点击节点展开/收起子图,或者切换视图。这需要 SVG 里嵌 JavaScript,复杂度高一些,但体验好。适合大型系统的架构图,因为一张图放不下所有细节,交互式探索让用户按需展开。

7. 我踩过的坑与实操心得

7.1 不要一开始就追求完美模型

我刚开始做的时候,想把所有信息都放进模型:服务、接口、数据库表、字段、负责人、SLA、监控指标……结果模型文件巨大,维护累,渲染慢,最后没人愿意改。后来砍掉大部分属性,只留影响渲染和查询的,模型瘦身到原来的三分之一,维护意愿立刻上来了。

教训是:模型是活的,先跑起来再优化。第一版只放实体和关系,能出图就行。后面发现缺什么再加什么。不要一开始就设计完美 schema,因为你对需求的理解会变。

7.2 一致性检查脚本要早写

我们是一年后才写一致性检查脚本的,之前一直靠人工 review,漏了不少。写完之后发现了几十处不一致,修了好几天。如果一开始就写,这些不一致根本不会积累。

脚本不用很复杂,能对比代码调用和模型关系就行。静态分析工具很多语言都有,提取调用关系不难。关键是进 CI,每次提交都跑,不一致就失败。这样问题在源头就被拦住。

7.3 布局调优要留出时间

我第一次渲染出来的图,节点挤在一起,线交叉得像蜘蛛网。调布局花了整整一天,试了各种参数组合。后来发现,布局调优是必要投入,因为图是给人看的,看不清就白做了。建议在项目初期留出时间调布局,把参数固化到视图定义里,后面就不用反复调了。

7.4 文档要跟图一起更新

图更新了,文档没更新,读者会困惑。我们的做法是:文档里的实体表格从模型自动生成,跟图同源。这样图变了,表格自动变,不会脱节。文档里的文字说明(比如“订单服务负责处理订单生命周期”)手工维护,但会定期 review,确保跟图一致。

7.5 团队推广要循序渐进

一开始不要强制所有人用。先在一个小团队试点,跑通了再推广。推广时提供模板和脚本,降低上手成本。我们做了个“新建服务”的脚手架,跑一下自动生成模型文件片段,开发只需要填几个字段。这样大家觉得方便,才愿意用。

最后分享一个小技巧:给模型文件加注释。YAML 支持注释,可以在实体或关系旁边写“为什么这么设计”“什么时候加的”。这些注释在 diff 时能看到,帮助理解变更背景。我们模型文件里有很多这样的注释,新人看的时候能快速理解历史决策。

这个内容后续还可以这样扩展:把模型跟服务目录打通,自动同步服务元数据;或者把渲染集成到 IDE 插件,写代码时实时预览架构图。方向很多,核心是模型驱动这套思路,一旦跑通,扩展空间很大。

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

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

立即咨询