系统分析 — 让系统先在纸上跑一遍

一句话:系统分析就是在写代码之前,用四张图(模型、状态、契约、时序)把系统跑一遍,把「没想到」提前变成「写下来」。

🎯 这 20% 能解决哪些问题

你遇到的困惑本质原因马上能改的动作
联调时接口字段对不上契约在代码里,不在文档里先定 OpenAPI,前后端按同一份 schema 生成/校验
订单状态越来越乱,谁都能改没有状态机,业务代码随手更新 status列出状态迁移表,未登记的迁移一律拒绝
一个功能改十个地方领域概念没建模,逻辑散在 Controller先找名词建实体,再让每个实体自己管自己的规则
事务边界说不清,数据对不上没分析「哪几步必须一起成功」每个用例标出事务范围与补偿动作
需求评审过了,设计评审还在改需求系统分析缺失,把设计问题带回需求用状态机与异常分支反推需求漏洞

一、分工:需求说「做什么」,系统分析说「靠什么撑住」

维度需求分析系统分析
输出问题用户看到什么系统内部怎么组织
核心产物验收标准、非功能指标领域模型、状态机、接口契约、事务边界
判定标准可验收可编码、可测试、可演进
典型残留风险需求遗漏边界模糊、异常未定义、并发未考虑

判据:如果开发看完文档还要问「这个字段谁维护」「失败算哪一步」,说明系统分析没做完。


二、领域建模:名词找实体,动词找行为

flowchart LR
    N["需求文本<br/>名词/动词"] --> E["实体 Entity<br/>有身份、会变化"]
    N --> V["值对象 Value Object<br/>无身份、不可变"]
    N --> A["聚合 Aggregate<br/>一致性边界"]
    A --> R["仓储 Repository<br/>存取入口"]
    E --> S["服务 Service<br/>跨聚合流程"]

2.1 四个判断标准

概念判断标准例子
实体换一个属性仍是「同一个东西」订单(有订单号)
值对象属性全等就算同一个金额、地址、时间段
聚合必须同时一致的一组对象,只有一个入口订单 + 订单项
领域服务逻辑跨多个聚合,放谁身上都不合适下单扣库存 + 生成支付单

2.2 建模时最常漏的三件事

  1. 谁拥有这个字段:一个字段只能有一个写入方,其他方通过接口改。
  2. 历史与快照:价格、地址这类会变的字段,业务单据要存快照,否则对账必错。
  3. 软删除与唯一约束的冲突:删除后同一手机号能否再注册,必须明确。

三、状态机:任何 status 字段都必须配迁移表

stateDiagram-v2
    [*] --> 待支付
    待支付 --> 已支付: 支付成功
    待支付 --> 已取消: 超时或用户取消
    已支付 --> 已发货: 仓库出库
    已发货 --> 已完成: 用户确认
    已支付 --> 退款中: 用户申请退款
    退款中 --> 已退款: 退款成功
    已完成 --> [*]
    已退款 --> [*]

迁移表模板(必须四列):

从 → 到谁能触发前置条件副作用(必须可补偿)
待支付 → 已支付支付回调金额一致、未超时扣库存、发通知、记流水
待支付 → 已取消定时任务/用户超过 30 分钟未支付释放库存
已支付 → 退款中用户未发货冻结结算

三条铁律:

  • 未在迁移表里出现的状态变化,代码层直接拒绝(用枚举 + 断言,不要散落 if)。
  • 有副作用的迁移必须幂等:同一回调重复到达只生效一次(用业务唯一键)。
  • 不可逆迁移(已退款、已完成)不允许代码回改,只能新增逆向单据。

四、接口契约:先定 schema,再写实现

契约要素必须写明常见坑
路径与动作资源用名词,动作靠 HTTP 方法URL 里塞动词 getUser
入参类型、必填、长度、格式、默认值只写字段名不写约束
出参字段含义、单位、时区、精度金额用浮点、时间无时区
错误统一结构 + 业务错误码表全部返回 500 或只返回字符串
幂等是否支持重复提交、靠什么键支付/下单无幂等键
版本破坏性变更如何升级直接改字段含义
统一响应:{ "code": 0, "message": "ok", "data": {...}, "trace_id": "..." }
业务错误:{ "code": 40301, "message": "库存不足", "data": null }

判据:把 OpenAPI 文件直接交给前端与测试,双方不读实现代码就能开工——契约才算成立。


五、时序与事务边界:把失败路径画出来

sequenceDiagram
    participant U as 用户
    participant O as 订单服务
    participant S as 库存服务
    participant P as 支付网关
    U->>O: 提交订单
    O->>S: 预占库存
    alt 预占成功
        O->>P: 创建支付单
        P-->>O: 返回支付链接
        O-->>U: 待支付
    else 预占失败
        O-->>U: 库存不足
    end
    Note over O,S: 超时未支付需补偿释放库存
要标出的东西为什么
每一步的超时时间没有超时就没有确定的失败
失败后的补偿动作分布式下无法回滚,只能补偿
幂等键重试与回调重复是常态
事务范围一次事务只包一个聚合,跨聚合用最终一致

核心规律:能用「本地事务 + 状态机 + 补偿任务」解决的,不要上分布式事务。


六、容量与关键路径估算

估算项公式 / 方法用途
峰值 QPSDAU × 人均请求 ÷ 86400 × 峰值系数 3~5决定实例数与缓存
单请求数据量平均响应体 + 图片等静态资源决定带宽
日增存储日订单数 × 单条大小 × 副本数决定磁盘与归档策略
热数据比例近 7 天访问量 ÷ 总量决定缓存命中率目标
关键路径长度一次请求串行调用数超过 5 段就要并行或合并

七、系统分析交付物清单

产物最小要求谁看
领域模型图5–15 个核心实体 + 聚合边界开发
状态迁移表每个状态字段一张开发 + 测试
接口契约OpenAPI/JSON Schema + 错误码表前端 + 测试
关键时序图只画跨系统 / 跨服务的 3–5 条主链路全体
数据模型表 + 关键索引 + 保留策略开发 + 运维
容量估算峰值 QPS、日增存储两个数架构 + 运维

🎓 实战场景(TIL)

  • 场景:上线后发现「已退款又变成已完成」的脏数据。本质:状态迁移只写了主干,没列全「哪个状态能到哪个状态」。要点:先画状态机表(当前态 × 事 → 次态),非法组合统一报错,并将表直接变成代码里的守卫断言。
  • 场景:前后端联调天天改字段。本质:接口只有口头约定,没有契约与版本。要点:先交 OpenAPI/类型定义再开工,字段变更走版本或新增字段,禁止改语义。
  • 场景:一个请求里发了三个远程调用,偶发一半成功。本质:事务边界把外部调用包进了本地事务。要点:事务只保护本地数据;远程动作改成「提交后事件 + 可重试任务」并保证幂等。

📋 一页速查

我要检查通过标准
领域模型每个核心名词都有归属实体;每个聚合只有一个写入口
状态机有迁移表;未登记迁移被拒绝;副作用可补偿
接口契约字段有类型与约束;错误码成表;幂等键明确
事务边界一事务一聚合;跨聚合有补偿任务与重试上限
异常路径超时、重复、乱序三种情况都有明确结果
数据保留每个表有增长估算与归档/清理策略
评审可开工前端能只凭契约开工;测试能只凭状态表写用例

🔗 相关链接