今天去翻公司的 Confluence 维基,本想找一下支付系统的调用拓扑图。结果点开一看,最近一次更新时间竟然定格在两年前。图里的核心中间件早就被改掉了,有些下游接口也早已废弃,而新拆出来的微服务在图上甚至查无此人。
看着这种早已过期的 Visio 框图,心里真是一万只羊驼奔腾而过。这是绝大多数工程团队的通病:图和代码是完全脱节的。新需求一催,大家提了代码就跑,谁还会有闲心去登录绘图网站、调整框图对齐、重新导出图片再贴进 Wiki 页面?
手动绘图的维护成本简直就是灾难。为了根治这个大坑,我们开始推行“架构即代码”(Architecture-as-Code)理念,将 C4 Model 设计规范与 Git 仓库绑定,直接用 Markdown + Mermaid 语法在代码库中书写系统架构图,实现系统设计与代码改动的“同生共死”。
下面是我们项目中实际落地的 C4 Container 级别系统设计图。通过这种文本声明式的定义,任何开发者改动系统链路时,只需要用几行文本就能顺手把架构图修好:
graph TD
%% 样式定义
classDef client fill:#D4E6F1,stroke:#2980B9,stroke-width:2px;
classDef gateway fill:#FADBD8,stroke:#C0392B,stroke-width:2px;
classDef service fill:#FCF3CF,stroke:#F39C12,stroke-width:2px;
classDef storage fill:#D1F2EB,stroke:#16A085,stroke-width:2px;
%% 节点定义
Client["📱 客户端 App (React Native)"]:::client
Gateway["🛡️ API 网关 (Kong / Envoy)"]:::gateway
subgraph CoreDomain ["核心后端服务域"]
OrderService["📦 订单微服务 (NestJS)"]:::service
PaymentService["💳 支付微服务 (Go / Zap)"]:::service
AuthService["🔑 认证鉴权中心 (Rust)"]:::service
end
Redis["⚡ 分布式缓存 (Redis Cluster)"]:::storage
MySQL["💾 主业务数据库 (MySQL Cluster)"]:::storage
MsgQueue["📬 消息队列 (Apache Kafka)"]:::storage
%% 链路定义
Client -->|HTTPS / GraphQL| Gateway
Gateway -->|HTTP / JSON / JWT| AuthService
Gateway -->|RPC / gRPC| OrderService
Gateway -->|RPC / gRPC| PaymentService
OrderService -->|分布式锁 / 查单缓存| Redis
OrderService -->|事务读写| MySQL
OrderService -->|发送订单事件| MsgQueue
PaymentService -->|扣款流水入库| MySQL
PaymentService -->|发送扣费成功事件| MsgQueue
MsgQueue -.->|异步消费更新状态| OrderService
linkStyle default interpolate basis;
把这段文本贴在代码库根目录的 ARCHITECTURE.md 里面,在 VS Code、GitHub 或 GitLab 里都可以开箱即用直观渲染。在提 PR 修改后端链路时,代码变更和 Mermaid 的文本描述必须放在同一个 PR 里过 Code Review。有了 Git 历史提交做背书,再也不用担心“代码改了,文档没改”的扯皮问题。
通过引入 C4 Model 把复杂的系统拆解为 Context(上下文)、Container(容器/服务)、Component(组件)和 Code(代码)四个由粗到细的层次,我们过滤掉了无关的噪音,只专注于服务层级和数据流的走向。
不过,虽然 Mermaid 极其省事,但也有架构师吐槽它的排版机制太黑盒,有时候多画一条线,整张图的布局就会直接崩溃,丑得没法看。
大家在团队中是如何维护架构图的?是继续坚守 Draw.io / Visio,还是引入了像 Structurizr 这种更重度的 Architecture-as-Code 方案?欢迎分享经验。