
1. 从“要什么”到“做什么”这条鸿沟才是架构设计的生死线我这些年看过太多系统烂在需求分析和软件设计的交接地带。开发团队抱怨产品经理需求说不清产品经理吐槽开发听不懂人话架构师夹在中间左右为难——而实际上问题往往出在所有人都把“需求分析”当成一次谈话而不是一个需要严格产出物的工程阶段。尤其是做架构设计的人如果不在需求阶段就把边界、约束、质量属性和优先级逼问清楚后面所有设计决策都是沙上建塔。这篇笔记围绕“系统架构设计笔记34—— 需求分析与软件设计”这条主线聊聊我从需求到设计之间的那座桥到底该怎么搭需求分析到底在分析什么、软件设计到底在设计什么、两者之间有没有一套可复用的映射方法以及我在真实项目里踩过的那些坑。适合正在从编码往架构方向走的开发者也适合被“需求总变”折磨得想摔键盘的技术负责人。先给一个核心定调需求分析不是在“记录用户说的话”软件设计也不是在“画架构图”。前者是把业务诉求转化成有边界、可验证、能排优先级的问题集合后者是把这些问题集合映射成一组能应对变化的工程决策。两者的分界线不是文档交接而是“问题定义清楚”和“方案选择开始”的那一刻。2. 需求分析的三个层次业务需求、用户需求、功能需求的拆法很多团队的需求文档只有一个层次“用户要能查订单”。这句话看着清楚实际上什么也没说。查订单是哪个用户查自己的订单还是查所有订单按照什么条件筛查询超时能容忍多久数据量到多少时不能超过三秒这些问题的答案直接决定你后续是加一张表、加一个缓存还是直接上一套搜索引擎。2.1 业务需求先回答“为什么做”和“不做什么”业务需求描述的是系统在商业或组织层面要达到的目标。它通常不是功能描述而是价值描述。比如“降低客服人工介入率”“把订单处理时效从一天缩到一小时”“支持千人同时在线选课不卡顿”。这些目标写下来之后第一个要追问的是哪些事我们明确不做什么我在一个模拟项目X里就吃过这个亏。业务方当时说“我们要做一个数据看板”我们原型图做了十几版结果最后一次评审时对方才说“我们其实只关心日活和留存其他指标后续再说”。那之前所有图表设计和口径讨论都是纯浪费。后来我养成一个习惯需求分析的第一轮访谈只会问三类问题——这件事为什么要做做成了什么样算赢有什么明确不允许发生的这三问能把需求边界瞬间从“无限可能”压缩到一个实际范围里。2.2 用户需求从角色视角梳理使用场景用户需求的核心是角色不是功能。同样一个“审批”功能普通员工看到的是“提交申请、查进度”审批人看到的是“待办列表、批量通过”管理员看到的是“审批流转配置、超时提醒”。如果需求描述里没有角色前缀这个需求就不合格。实操中我建议大家用“用户故事用例”双层描述法。用户故事负责讲故事用在和业务方沟通的场景里格式固定为“作为某角色我希望某功能以便某价值”。比如“作为审批人我希望在手机端一键批量通过周报以便在出差路上也能快速处理”。用例则负责兜底完整性把前置条件、主流程、异常分支、扩展流程都写清楚。用户故事管对齐、用例管约束两者缺一不可。2.3 功能需求把场景翻译成可验证的系统行为第三层才是功能需求。它的产出物应该是“在什么条件下系统针对什么输入产生什么输出满足什么性能要求”。比如“审批人在待办列表勾选多条记录后点击批量通过按钮系统应在两秒内完成所有记录的审批状态更新并返回结果视图若其中任意一条失败失败条目需显式标红且不影响其余成功条目”。这个粒度是架构设计能开工的最低门槛。低于这个粒度你会发现自己根本没法判断到底该用同步接口还是异步消息、要不要引入事务、需不需要做幂等设计。3. 需求分析的核心产出物不只是文档更是决策依据3.1 需求规格说明的黄金结构一份能直接指导设计的《需求规格说明》至少包含六个部分范围与目标含非目标、角色与术语表、功能性需求列表编号描述验收标准、非功能性需求性能、可用性、安全、可维护性等量化指标、约束与假设技术栈限制、合规要求、第三方依赖、优先级标注必须做、应该做、可延后。对架构师来说最重要的部分是“非功能性需求和约束”因为这两者才是影响架构选型的关键变量。功能需求决定系统有哪些模块而非功能需求决定模块之间怎么通信、数据怎么存储、系统要设计成什么形态。3.2 需求验证在写设计之前先回答五个问题很多团队跳过需求验证直接进入设计结果就是设计评审变成需求澄清会。我建议在需求阶段结束前做一次五问自检每个功能需求是否都有明确的业务价值来源说不出来源的删掉或者降级。每个非功能需求是否都有量化指标和验收方法不能量化的比如“性能要好”直接打回重写。每个需求是否都有优先级没有优先级意味着所有需求都重要也就意味着面对冲突时无法取舍。是否有明确的反面需求即确定不做的事情需求之间是否存在定义冲突比如“所有数据实时可见”和“海量历史数据低成本存储”天然冲突必须当面把口径对齐。这五个问题全部有答案之后软件设计才算真正有资格开工。4. 软件设计不是画图是决策建模软件设计阶段的本质是回答一个问题在给定的需求和约束下我们选择用什么样的结构去承载这个系统使它能够满足功能要求并且在可预见的未来范围内能以可接受的成本应对变化。4.1 模块划分的底层逻辑高内聚与低耦合的真实含义模块划分几乎是软件设计中最早也最容易做错的决策。很多人把模块按“功能”切比如用户模块、订单模块、支付模块但真正合理的切法是按“变化的轴心”切——把那些会因同一个业务原因而变化、且变化频率相近的东西放进一个模块。举一个通俗的例子一个商城的订单状态机和支付回调逻辑如果按功能边界它们一个属于订单模块、一个属于支付模块但按变化轴心它们高度相关——支付回调的结果会直接驱动订单状态变化。把这类紧密协作的代码拆到不同模块每次联调都要跨团队沟通改一次状态流转牵全身这是典型的“功能内聚”陷阱。好的模块划分应该做到一个模块内部的元素因为同一个原因而被修改模块之间因为不同的原因而被解耦。4.2 接口设计先定协议再定实现架构师最容易犯的错是先讨论用什么框架、用什么存储再回头定接口。正确的顺序反过来先定模块之间交互的接口协议再定模块内部的实现方案。接口协议要回答的内容包括是同步调用还是异步消息是请求-响应还是事件驱动数据格式是JSON还是protobuf错误码体系怎么设计幂等性由谁保障超时和重试策略是什么这些决策不在需求阶段被量化非功能指标约束住到设计阶段就会变成“摸石头过河”。我个人的经验是当两个模块需要频繁双向交互、且对实时性要求高时优先考虑同步接口当交互是一方通知另一方、且允许一定的处理延迟时优先考虑异步消息。接口的粒度要按“业务能力”设计不要按“数据表”设计。一个更新用户手机号的接口如果暴露为updateByIdAndPhoneAndStatus也是一样这种接口跟着表结构走表一改接口就爆长期维护成本极高。正确做法是面向业务动作设计比如updateUserProfile内部自己管理哪些字段允许更新、哪些字段需要联动校验。4.3 数据模型需求映射到存储的翻译手法数据模型设计的高下之分在于你是否理解了数据的“生命周期”。同样的用户数据在读多写少、实时性要求高的场景里适合放缓存在需要强事务保证的业务核心场景里适合放关系型数据库并设计好索引在需要灵活字段扩展的场景里可能需要文档型存储在需要复杂关系查询的场景里又得考虑图数据库。需求驱动数据模型的关键方法是用“实体-生命周期-访问路径”三个维度的交叉来验证。先用需求文档中的名词提取实体然后定义每个实体的状态和流转最后画出核心访问路径。一条订单记录从生成到取消历经哪些状态、每一步被哪些模块读写、读写频率和数据量多少这些信息全部从需求分析中来。如果需求阶段没梳理这层数据建模就只能靠拍脑门。5. 从需求到设计的映射实操一个完整的推演案例5.1 原始需求描述与问题为了把抽象的方法落到地上我构造一个虚拟又非常典型的需求场景业务方提出做一个“内部培训报名系统支持发布课程、员工报名、管理员审核、报名成功通知还要能看报名统计报表”。看着很简单但细究之下问题一个接一个员工指的是全员还是某些部门课程发布是否要审批报名是否有名额上限超过之后要不要排队统计报表的实时性要求是多少通知走邮件、短信、站内信还是都要员工离职后历史报名数据如何处理这些问题不完备软件设计就是空中楼阁。5.2 需求分解与确认清单以这个场景为例我在实际项目中会先产出一张需求完整度检查清单不完全列举如下角色定义课程发布员、报名员工、审核管理员、系统管理员是否算同一人不同角色权限边界在哪业务规则课程是否允许取消取消后名额是否释放报名是否允许多人同时抢最后一个名额冲突时怎么办数据保留历史报名记录是否需要审计数据保留多久是否可以归档性能要求报名高峰期并发量预估多少报表查询容忍几秒延迟通知推送成功率要求几个九平台约束需要支持PC、手机浏览器还是App是否需要对接企业已有的组织架构和单点登录体系5.3 设计决策推导过程基于上述需求确认结果我给出一个完整的设计推演第一模块划分上按变化轴心分出“课程管理”“报名流程”“通知中心”“统计报表”四个核心域。报名流程是需求变化的高发区业务规则调整基本都在这里把它独立出来后续规则变化就不会污染课程管理模块。第二接口设计上报名模块和通知中心之间选择异步消息。原因是通知这个动作允许延迟且通知方式可能频繁变化——今天用站内信明天加邮件后天又要加企业微信推送。同步调用会把通知的失败传导到报名主流程上这是不能忍的。异步化之后报名主流程只需要在事务里发一条消息通知的失败不再影响报名结果。第三数据模型上报名记录表必须包含课程ID、员工ID、报名时间、状态、来源、审核人、审核时间、审核意见等完整字段索引设计上则要覆盖高频查询路径——按课程查报名列表、按员工查报名记录、按状态查待审核队列。报表数据如果要求实时性高直接查业务库如果允许五分钟延迟可以单独走汇总表或物化视图避免报表查询拖垮核心流程。第四非功能需求上的取舍是关键。并发抢课场景假设峰值并发是500人同时报名同一门课程后台库存只有80个名额。这里就必须在需求阶段确定超过名额的请求返回“已满”还是进入等待队列如果进入等待队列释放名额后补位策略是先到先得还是按优先级这个决策直接影响数据库的行锁行为和队列方案的设计。5.4 代码层面的落地示范设计再漂亮落不到代码上就是空话。我写一个简化版的报名处理流程演示需求约束如何映射到实现# 课程报名处理名额与并发 def apply_course(course_id, employee_id, source): # 1. 幂等校验同一员工对同一课程只允许一条有效报名 if Registration.exist_valid_application(course_id, employee_id): raise BusinessException(不能重复报名) # 2. 在当前事务内锁定课程名额悲观锁方案 # 这里不能用先查再更新的写法会丢更新 course Course.select_for_update().get(idcourse_id) if course.current_count course.capacity: # 需求明确超过名额且不排队时直接失败 raise BusinessException(课程名额已满) # 3. 更新计数 插入报名记录同一事务保证一致性 course.current_count 1 course.save() registration Registration.create( course_idcourse_id, employee_idemployee_id, statuspending_review, sourcesource, ) # 4. 发送异步通知消息不阻塞主事务提交 notify_queue.publish( event_typeregistration_created, payloadregistration.to_dict(), )注意这个实现里merge不了所有细节但核心逻辑展示了需求规则怎么变成技术约束。第一行的幂等校验来自需求里“同一员工不能重复报名”第二行的行锁来自需求里“500人抢80个名额”的并发场景“不能先查再更新”这条经验则来自实际踩坑——两个客户端同时查到余量80各自执行更新最后就超卖了。6. 架构设计中必须面对的“需求变化”不是对抗而是预案需求不变的项目不存在。软件设计不是要把未来所有变化都提前实现而是要把变化可能影响的区域隔离在局部让一处改动不会像多米诺骨牌一样推倒整栋楼。6.1 变化点分析在需求阶段就圈出“易变区”需求评审时特别留意哪些描述用了“先”“暂时”“后续”这类词。比如“先做管理后台后续看情况开放给供应商自助服务”这句话的信息量巨大——管理后台和供应商门户很可能共用同一些数据但面向的使用者安全级别完全不同身份认证方式和数据可见范围也会不同。如果需求阶段把这些“后续”信息记录下来界面的设计就能提前预留扩展点而不是等开发到一半才突然发现要加一套外部认证。我的实操方法是在需求文档里单独建一个“潜在变化与扩展假设”章节。每次业务方提到未来可能性就记录一条并标注“影响范围”和“触发概率”。这个表不需要做得面面俱到但凡是标记为高影响、高概率的设计时必须预留扩展接口或保留合理的索引结构标记为低影响、低概率的按当前需求实现不做过度设计。6.2 对抗“过度设计”与“设计不足”的双向风险过度设计和设计不足是一对双向风险架构师需要同时警惕。过度设计的典型表现是业务方只需要一个20人以内的内部工具你非要上一套微服务加消息队列加容器编排平台。设计不足的典型表现是系统明知道要做成面向几千用户的外部连锁门店系统你按单机单库的规模设计连基本的分页、断点续传、缓存失效策略都没部署。前者的成本是维护负担和交付延后后者的成本是上线即重构。怎么拿捏这个度我的判断标准始终锚定在需求分析里那五个量化指标上用户规模、数据规模、并发量、可用性要求、变更频率。用户规模决定你需不需要集群数据规模决定你需不需要分库分表并发量决定你需不需要缓存和限流可用性要求决定你需不需要多机房和故障切换变更频率决定你需不需要花大力气做模块解耦和接口版本化。6.3 需求变更的规范化流程改需求可以改流程不行任何系统都会遇到需求变更真正毁掉设计的是“无流程变更”。业务方口头说一句“把某个字段的下拉改成可输入”产品经理转脸告诉开发“就改一下”开发两分钟改完接口没有评估影响面没有回归测试然后某天凌晨线上数据异常一查才发现刚才那两分钟把校验逻辑绕过了。我惯用的处理方式很简单任何变更请求必须走一张变更单内容包括变更描述、变更原因、影响模块、涉及接口、预计工作量、回归测试范围、上线方式。这七个字段填写完整之前不动代码。这样做不是为了繁琐流程而是为了把“需求分析和软件设计”的核心思想贯彻到变更环节——每次变更都是一次小型的需求分析和设计评审只是范围更小、速度更快。7. 我在做的事用一张“需求-设计对照矩阵”收口所有决策最后分享一个我一直在用的落地工具暂称“需求-设计对照矩阵”。做法是表格行放需求编号列放设计决策项每个交叉格里记录“此需求对这项设计的影响”。设计决策项一般包含模块归属、接口协议、数据存储方式、依赖关系、性能保障策略、扩展预留点六项。这个矩阵的威力在于它能强制你证明每一个设计决策都有需求依据。如果某个设计决策在矩阵里没有任何一条需求能解释它说明这个决策是拍脑门的需要回到需求池里重新核对。反过来如果某条需求在矩阵里找不到要映射的设计决策说明这条需求可能被遗漏了或在设计中没有承接。比如用这个矩阵检查那个培训报名系统的设计需求R003“审核员要能看到待审核报名列表”在设计决策里的对应条目是“报名记录表需建立statecourse_id组合索引、审核列表接口需分页、状态变更需写入操作日志”。有了这条映射开发阶段再有人问“为什么这里要加这个索引”“为什么这个接口要分页”直接查矩阵就行不用再从原理讲起。我在实际使用这个矩阵时发现它另一个隐形收益是团队对齐效率的提升。过去设计评审会上争论的是“我觉得”“我觉得”上了矩阵之后争论变成了“R012没体现这个约束”“D005这个决策的需求依据在R018”。话语体系变了讨论质量就完全不同架构师从“裁判”变成了“主持人”整个团队也能更专注于需求与设计本身的一致性问题而不是来回掰扯主观意见。关于需求分析与软件设计的关系我的核心体会是这两个阶段不是流程上下游那么简单它们是同一件事的两面——一个负责把问题定义清楚一个负责把答案设计正确。问题定义错答案再精美也是浪费答案设计差问题定义再准也交付不了。把这两件事之间的映射关系做严谨、做显性化比记住任何架构模式都更有用也是系统设计不再返工、不再翻车的关键所在。