做架构评审这些年我常在一开始问对方一个问题“你这张图想给谁看”很多人会愣一下然后开始重新组织语言。UML五大视图也就是常说的41视图模型本质上就是在回答这个问题一个系统至少得从5个不同视角去描述每个视角对应不同的利益干系人、不同的关注点、不同的一组图把它们拼在一起才算是完整蓝图。这篇笔记适合软件架构师、后端开发者、技术负责人以及正在学UML或准备系统架构设计考试的学生。读完你会明白每个视图解决什么问题、各自用什么图、视图之间怎么保持一致以及在实际项目里怎么把这套方法用起来。1. 为什么是4个视图再加1个场景1.1 不同角色看到的系统本来就不一样同一个系统在用户眼里是一组功能在架构师眼里是一堆类和接口在运维眼里是一批进程和服务器在测试眼里是模块边界和接口契约。谁的视角都不能代表全部但谁的视角都不能缺。举个很常见的例子某订单系统上线后出现支付超时问题。用户看到的是“付款转圈圈”开发查了半天发现是订单服务和库存服务之间那个同步调用没有做超时控制运维一看监控发现两台应用服务器负载不均请求全打在老节点上。这里已经涉及了功能、代码结构、并发交互、部署拓扑四个不同层面的问题单靠一张架构图根本说不清楚。这就是41视图模型的出发点逻辑视图回答“系统由哪些抽象协作组成”进程视图回答“运行时如何并发和通信”开发视图回答“代码怎么组织、模块怎么划分”物理视图回答“系统部署在哪里、节点之间怎么连”最后再用一个场景视图把所有东西串起来。1.2 越复杂的系统越不能用一张图描述很多团队习惯画一张“大而全”的架构图把类、接口、服务、数据库、服务器全堆在一起结果图越来越满能看懂的人越来越少。这不是绘图水平问题而是信息维度太多了。打个比方盖一栋房子需要平面图、立面图、结构图、水电图、效果图每张图服务于一个施工阶段或一类工程师。如果只给施工队一张效果图他们完全不知道怎么砌墙布线。软件系统比房子更抽象它没有物理实体可以“围着看一圈”只能用不同视角的模型交叉表达。41里那个“4”说的就是4个正交的基础视图逻辑视图管静态结构、进程视图管动态行为、开发视图管代码组织、物理视图管部署环境。每张视图都只承载一部分信息组合起来才不会信息爆炸也不会丢失关键细节。1.3 5个视图不是画5张独立的图而是互相约束的一整套模型如果只是“画5张图”那这套模型也没什么特别。真正有价值的是视图之间的映射关系逻辑视图里的一个类最终会跑在进程视图的某个线程里被打包进开发视图的某个组件中再部署到物理视图的某个节点上。我见过很多团队每个视图都画得挺漂亮但一追问“类A部署在哪台机器上”“订单服务挂了会影响哪些用例”就答不上来了。这说明视图之间没有建立追踪关系。41视图模型要求你在设计每个视图时都要同时想清楚它和其他视图的接口在哪里这样任何一处变更都能顺着链路评估影响范围。2. 四个基础视图逐一拆解2.1 逻辑视图系统的骨架结构逻辑视图从抽象设计角度描述系统重点是把职责划分清楚。这里的主角是类、对象、接口、包以及它们之间的关系常用图包括类图、对象图、状态图和包图。画逻辑视图时我习惯先识别三种类型的分析类实体类、边界类、控制类。实体类负责承载核心数据和业务规则比如订单、支付记录边界类负责与外部交互比如接口控制器、页面模型控制类负责编排业务逻辑比如下单流程处理器。用这种思路画出来的类图层次感会好很多不会所有类都平铺在一张图上。以某订单系统为例逻辑视图至少要有订单聚合、订单项、商品库存、支付单这四类核心对象。它们之间的关系要画清楚订单和订单项是聚合关系订单项指向商品支付单依赖订单。加上操作类图才真正有用——比如扣库存的方法必须放在商品类上而不是放在订单服务里否则后面做并发控制时会很别扭。这里有个新手特别容易踩的坑把逻辑视图当成数据库ER图来画。逻辑视图里的类不都对应数据库表尤其是控制类和边界类它们只是运行时协作单元。过早纠结字段与表结构会把架构设计拖回详细设计的泥潭。2.2 进程视图并发与协作的控制流如果说逻辑视图描述的是“系统里有什么”进程视图描述的就是“它们怎么活起来”。它关注进程、线程、并发、同步、通信、事件调度这些运行时问题常用图是活动图、时序图和协作图。很多并发问题在逻辑视图里看不出来。比如两个线程同时调用商品的扣库存方法逻辑视图上只是“商品类有个方法叫扣库存”但进程视图里必须明确这个方法有没有加锁用乐观锁还是悲观锁失败了怎么重试库存扣减和订单创建是同步执行还是异步削峰我处理这类问题时首选活动图加泳道。泳道按角色或系统划分比如用户、前端系统、订单服务、库存服务。先画出主流程创建订单、扣库存、支付。然后在扣库存这一步用分叉汇合符号表示“订单服务和库存服务并行处理”再在汇合点标注等待条件。这样整个流程的控制流非常直观评审时大家一眼就能看出哪里可能会死锁、哪里需要超时。时序图则适合表达对象之间的消息顺序。画时序图时消息顺序就是代码执行顺序但注意只画关键交互不要画出所有方法调用。进程视图的目的不是记录代码细节而是暴露并发风险和数据一致性问题。2.3 开发视图代码仓库内的组织秩序开发视图面向程序员描述代码如何组织、模块如何划分、构建单元之间如何依赖。常用图是包图和组件图。包图是逻辑视图类图的下沉细化。逻辑视图里定义了一堆类开发视图要把这些类归入不同的包比如控制器层、应用服务层、领域层、基础设施层。划分包时我遵循两个原则依赖只能从上往下不能反向依赖被依赖多的稳定模块要放底层不能随意改动。循环依赖是最常见的头痛问题。A包依赖B包B包又依赖A包改一处两边都崩。解决办法是引入更底层的抽象包把两者共同依赖的接口抽出来让A和B都依赖这个抽象。画包图时我会顺手用依赖分析脚本扫一遍代码看有没有违反依赖规则的边发现问题马上处理不然包图很快就和代码脱节。组件图则负责描述可交付单元比如一个Java项目里的service-api.jar、service-impl.jar或者一个微服务项目里的order-service、payment-service。组件图能清楚展示每个组件的对外接口和依赖关系是开发和运维都能看懂的“中间层”视图。2.4 物理视图跑在机器上的真实世界物理视图描述系统硬件节点、软件部署和节点间的通信路径核心图是部署图。节点包括应用服务器、数据库服务器、消息队列集群、负载均衡器甚至网关和CDN。画部署图不能只画个服务器方块加一条线。每个节点上要标明部署了什么组件或构件每条通信路径要标注协议和端口比如HTTP/8080、JDBC/3306。还要注意哪些节点是集群关系、哪些是主备关系、哪些是无状态可水平扩展的。实操中物理视图很容易和网络拓扑图混为一谈。网络拓扑画的是路由器、交换机、防火墙部署图画的是软件组件的分布。两者信息不同不要互相替代。另外物理视图必须保持更新尤其是云原生环境下容器数量动态变化部署图不用画到每个Pod粒度但至少要画出逻辑部署单元和关键的伸缩边界。3. 场景视图把所有视图串起来的那根线3.1 为什么“1”是场景而不是别的四个基础视图各讲各的如果没有人把它们组织起来架构就会变成一堆静态文件。场景视图承担的就是这个组织角色。场景是什么就是用户和系统交互的具体故事。比如“顾客创建订单并支付成功”就是一个场景它有主成功路径、扩展路径和异常路径。每个场景会在逻辑视图中触发一组对象协作在进程视图中触发一组并发行为在开发视图中映射到具体组件在物理视图中落到具体节点。一篇重要的需求文档如果没有场景架构师根本不知道从哪下手。所以场景视图常用用例图来描述系统边界和参与者关系再配合活动图、时序图细化每一个用例的行为。用例图是入口活动图和时序图才是场景的实体内容。3.2 从用例图推导架构的完整链条我推荐按下面这条路走先写用户故事或用例描述再画用例图从用例中识别参与者、前置条件、后置条件和主流程接着每个主流程画一张活动图找出需要协作的对象再把活动图的步骤转为时序图消息得到类之间的调用关系然后根据调用关系确认逻辑视图中需要的类最后把类归入包和组件确定并发模型完成部署设计。举例某“创建订单”用例。用例图里参与者是顾客用例包括创建订单、扣库存、支付。活动图显示顾客提交订单系统校验库存锁定库存创建订单跳转支付。转换到时序图时发现需要OrderController、OrderService、StockService、OrderRepository、PaymentClient这几个对象协作。于是逻辑视图的类图就有了雏形进程视图也会发现“锁定库存”必须是一个跨进程的原子操作开发视图里对应一组API和实现模块物理视图里则是应用服务与数据库服务的交互链路。这个过程就是41模型最核心的用法场景是需求四个视图是设计从场景出发推导设计再拿场景去验证设计。3.3 场景视图的常见误区第一个误区是把用例图画成需求说明书。用例图只表达参与者与用例的关系真正的行为细节必须通过用例描述或活动图补齐否则一个“支付”用例放在图上谁也不知道它背后有多少逻辑。第二个误区是用例粒度失控。有的团队把“输入手机号”“点击按钮”也画成用例图上一两百个节点完全失去架构视角。用例应该是对用户有价值的完整流程粒度控制在“一个参与者通过系统完成一个目标”这个级别。第三个误区是只画主成功路径。异常路径和扩展路径里藏着大量系统间协议和容错逻辑这些才是进程视图和物理视图最需要的内容。画场景时至少把主路径、一条扩展路径、一条异常路径都走一遍。4. 视图之间如何保持一致4.1 用追踪矩阵建立视图映射关系视图一致性不是靠自觉而是靠可追踪的映射关系。我见过最有效的方法是维护一张小型追踪矩阵行是一个个关键场景列是逻辑视图的主要类、进程视图的进程或线程、开发视图的组件、物理视图的节点。关键场景逻辑视图核心协作进程视图处理单元开发视图组件物理视图部署节点创建订单OrderController / OrderService / OrderRepository / Product订单服务进程 库存服务进程order-service.jar / stock-service.jarapp-1节点、stock-1节点支付回调PaymentClient / OrderService / PaymentRecord支付回调线程池payment-client.jargateway-1、app-1库存预警StockMonitor / StockRepository定时任务线程stock-monitor.jarbatch-1这张矩阵不需要覆盖所有类只需要覆盖关键业务场景和重大决策点。每次架构评审都拿这张表走一遍哪一行对不上就是哪里不同步要立刻修正。4.2 视图不同步的典型场景类图改了组件图没改。这是最常见的情况。开发在代码里把订单服务和支付服务合并成了一个组件但组件图还画着两个独立模块导致运维按旧图部署发布全乱。解决方式很简单组件图是交付文档代码合并后必须同步更新并走评审。部署图与真实环境不一致。某项目上线时引入了消息队列做异步削峰但部署图一直没更新后来排查性能问题运维按旧图以为是数据库瓶颈查了半天才发现消息队列才是关键路径。部署图要跟着基础设施变更走至少每个迭代更新一次。进程视图遗漏并发策略。逻辑视图里画了共享的库存对象进程视图却没有标注任何锁机制。结果压测一上来就超卖。进程视图是并发安全的唯一表达位置不能让开发自己去代码里猜。4.3 一致性靠流程、靠工具、靠基线先靠流程每个迭代安排一次架构走查选3到5个核心场景从用例开始把四个视图依次过一遍任何不一致当场改。走查不是走过场必须有架构负责人拍板。再靠工具如果团队用文本化建模工具比如PlantUML或类似方案视图就是代码纳入版本库管理别人改了视图可以通过diff看到变化点比在绘图工具里改图再截图发群靠谱得多。用桌面建模工具时也可以利用元素链接功能把类和组件关联但要看团队是否愿意维护这些关系。最后靠基线关键视图要有版本概念对外发布的架构图必须标记版本号和修订日期。没有基线的架构图过三个月就没人知道哪张是当前生效版本一致性问题会失控。5. 从零开始走一遍5个视图5.1 一个迷你订单系统的建模全流程我拿一个演示用的“订单管理系统”举例这个系统很小但足够走通流程。第一步画场景视图。参与者是顾客和管理员核心用例是下单、支付、查询订单、取消订单、商品管理。这里下单用例要先写用例描述前置条件是商品有货、用户已登录主流程是创建订单、扣库存、生成待支付单扩展流程是库存不足提示异常流程是支付超时取消订单。第二步画活动图。下单活动图分三个泳道用户、订单服务、库存服务。主流程执行到“扣库存”时订单服务和库存服务之间是同步调用但在高并发版本里改为异步消息。第三步画逻辑视图类图用PlantUML表达如下startuml class Order { -id: Long -createTime: Date -status: String addItem(item: OrderItem) calculateTotal(): BigDecimal } class OrderItem { -productId: Long -quantity: int -price: BigDecimal } class Product { -id: Long -stock: int deductStock(num: int): boolean } class Payment { -orderId: Long -amount: BigDecimal -status: String pay(): boolean } Order 1 *-- 1..* OrderItem Order *-- 1 Payment OrderItem -- Product enduml第四步画进程视图。这一步把“下单”拆成几个关键线程接收请求的Web线程池、处理业务的业务线程池、异步处理扣库存消息的MQ消费者线程。扣库存一定要用分布式锁或乐观锁否则并发下数据会错。第五步画开发视图。代码分成order-api、order-core、order-infra三个模块order-core是核心业务模块order-api对外暴露接口order-infra负责数据库和消息队列适配。组件依赖方向是api和infra都依赖coreapi不依赖infra这样替换技术栈时不需要动接口层。第六步画物理视图。部署结构是nginx负载均衡到两台应用节点应用节点连接MySQL主库和本地缓存RedisMQ集群独立部署。应用节点无状态可水平扩容数据库主从同步从库用于查询报表。这套流程在真实项目里不会一次走完通常先画场景和逻辑视图再根据架构评审结果细化进程和开发视图物理视图在部署方案确定后补上。关键是顺序不能乱场景定了结构才能定结构定了并发和部署才有依据。5.2 工具选型对比不同团队用不同工具我不强推某一个只分享一下选择时看重的几个维度。工具思路代表工具优点缺点适用场景文本建模PlantUML文本即图、版本库可diff、学习成本低、可自动生成复杂布局调整麻烦后端团队、文档走Git审查在线白板draw.io免费、上手快、协作方便图形与代码分离、不好做版本管理方案初稿、头脑风暴桌面建模StarUML轻量、支持多种UML图商用需授权个人学习、小型项目企业级建模Visual Paradigm功能全面、支持需求管理和追踪重、贵、学习曲线陡大型组织、合规审计选型原则很简单如果团队把架构图当交付物来维护优先选文本化工具如果只是评审时用一下在线白板就够了。不要把工具当成目标目标是有多少人能看懂并维护。5.3 微服务与云原生时代41还适用吗这个问题我经常被问到。41模型确实来自传统单体应用时代但它没有过时只是使用方式要调整。逻辑视图在微服务里下沉到单个服务内部每个服务有自己的领域模型和类图进程视图上升为服务间通信和分布式事务控制重点变成消息可靠性、幂等、超时和降级开发视图对应代码仓库、镜像和CI流水线物理视图对应容器编排集群、命名空间和节点池。场景视图依然担任入口只是从“一个系统”变成了“全链路”。这几个视图里进程视图的重要性明显提升。微服务架构下很多问题都出在服务间协作同步调用链太长、异步消息丢失、分布式事务边界不清。这些必须在进程视图里画清楚否则上线就是灾难。另外业界还有一种更轻量的C4模型把架构分为上下文、容器、组件、代码四个层级可以看作41在特定条件下的一种务实变体。它没有替代41但有时候更贴近团队习惯建议按项目实际复杂度去选。6. 高频问题与避坑经验6.1 遇到过的现实问题速查现象根因对策架构图三个月没人看图和代码脱节维护成本高只维护关键视图纳入评审流程类图又大又乱一个图塞了几十个类按包拆分一个包一张图用例图几百个节点用例粒度到操作级回到“参与者目标”粒度按模块分组并发问题反复出现进程视图没有同步机制每个关键用例必须画活动图和时序图部署图和实际不一致基础设施变更没人同步部署图跟随发布迭代每次发布时核对新同学看不懂架构图没有场景贯穿用场景视图做新员工架构入门6.2 几条行之有效的实操心得第一先定场景再定结构。没有场景的架构设计容易自嗨画了一堆类不知道哪个是核心。先写3到5个关键用例再从用例里提取类和协作关系效率高得多。第二每个迭代只更新与本次功能相关的视图。不用每次把所有视图都重画一遍。功能只改了订单模块就只更新订单相关的逻辑视图、时序图以及对应的组件和部署影响没必要为了更新而更新。第三删图比画图更难。视图的价值在评审和信息沟通不在数量。如果一个视图没有在评审会上被问过问题、没有被用来分析变更影响那它就是冗余的大胆删掉。第四图要能反向指导代码。我每次画完视图都会做一件事拿着图去看代码看实现是否对齐。如果图说A依赖B代码却是反的图就是错的。保持图和代码一致比画图技巧重要一百倍。最后说一点个人体会。我刚开始用41视图时总想着把每个视图画得又全又细结果花费大量时间在维护图上根本没发挥模型的作用。后来改成“场景驱动、按变更更新、评审时走查”的方式团队对架构讨论的质量明显提升。画图能力是基本功但真正拉开差距的是你能否让每一张图都在正确的时间、以正确的粒度、讲给正确的人听。