数据流图DFD实战:从系统分析到数据库设计的建模指南
系统分析阶段有一件事绕不开你得说清楚数据在系统里到底是怎么走的。数据流图DFD就是专门干这个的建模工具它的核心作用是用一张图把数据的流动路径完整地摆出来——数据从哪个外部来源进入系统、经过哪些处理环节、在哪里存放、最终又交给谁。做系统分析这几年我几乎每个项目都会先画一版DFD再动数据库和接口原因很简单这是整个团队对系统到底做什么达成共识最快的方式。这篇文章我打算把DFD的基本元素、分层画法、命名规范和踩坑经验一次讲透适合正在做需求分析、刚接触结构化设计的人也适合那些虽然听过DFD但一直没在项目里用起来的朋友。1. DFD到底解决什么问题为什么系统分析离不开它1.1 四个基本元素就可以表达一个完整的系统DFD之所以能成为系统分析阶段的核心建模工具最大的原因是它极度克制只用了四类元素就能把系统行为压缩成一张信息密度很高的图。很多第一次接触的人会觉得这东西不够高级但真正做过分析工作的人会明白建模工具的威力不在于符号多花哨而在于它能不能用一个统一的语言把所有干系人都拉上同一张桌子。这四类元素分别是外部实体、过程、数据存储和数据流。外部实体是系统边界之外、但和系统有数据往来的人、组织或外部系统。比如你在做一个电商系统客户、支付网关、物流平台这些都在系统外部但它们会往系统里送数据也会从系统里收数据。把外部实体单独拎出来本质上是在回答一个问题系统的边界在哪里过程是系统内部对数据的加工动作可能是录入、校验、计算、转换、汇总。每个过程至少要有一条输入数据流和一条输出数据流这条规则我会在后面详细讲它也是新手画DFD时最容易破功的地方。数据存储是数据停留的地方对应到现实里通常是数据库的表、文件或者其他持久化载体。DFD里的数据存储不关心表结构长什么样只关心数据在这里暂存或归档。数据流是带名字的箭头它表达数据在元素之间移动的方向和内容。整张DFD的信息量其实都压在这些箭头的命名上。我用表格把这四类元素捋一遍元素含义典型画法例子外部实体系统外的人、组织、系统矩形或带阴影的矩形客户、供应商、支付网关过程对数据的加工处理圆角矩形或圆形订单校验、库存扣减数据存储数据暂停或长期停留的地方开口矩形或双横线订单库、账务流水、日志文件数据流数据沿箭头方向流动带标签的箭头订单信息、支付结果这套符号体系在几十年的结构化分析方法里演化出过不同画法有的流派用圆角矩形表示过程有的用圆圈存储的画法也略有差别。我个人的建议是不用纠结你用了哪套关键是团队统一。只要约定好一套符号所有人按同一套规范画图就具备了真实的沟通价值。1.2 它和流程图不是一回事我经常在评审会上看到有人把DFD画成了业务流程图这是一个非常经典的混淆。流程图的核心动词是做它表达的是一个操作在什么条件下按什么顺序发生比如先判断库存有没有再决定是发货还是提示缺货这是控制逻辑。DFD的核心词是数据它只关心什么东西从哪来到哪去不关心第一步还是第二步也不关心分支条件是什么。打个比方流程图像是拍电影的分镜脚本每一场戏的顺序、人物的行动都要交代清楚DFD则像是物流公司的配送路线图它只标记货物从哪里发出、经过哪个中转站、最后送到哪个收货点至于中转站里的工人是先验货还是先搬货那是另一张图的事。这个区别带来的实操影响非常大。如果你在DFD里画了一条箭头叫点击提交按钮那就说明你画的不是数据流而是控制流。按钮点击是一个动作信号它不承载业务数据按DFD的规范这种控制流不应该出现。如果出现了图就会混入大量和数据处理无关的信息看上去很具体实际越看越乱。我见过不少项目的DFD之所以沦为摆设就是因为画成了披着数据流外衣的流程图。1.3 为什么说它是系统分析的骨架在我个人看来DFD在系统分析阶段的价值怎么强调都不过分。第一个价值是界定范围。一张上下文图摆出来项目涉及哪些外部系统、哪些角色、哪些数据接口边界一目了然大家不会再为这个功能到底算不算系统内的争论太久。第二个价值是暴露数据需求。数据字典和DFD是配套使用的DFD里每一条数据流的名字都必须能在数据字典里查到它的组成结构。比如你画了一条数据流叫订单信息那数据字典里就要写清楚订单信息由哪些字段组成。这个过程本身就是最原始的数据建模很多数据库设计阶段的字段缺失、类型含糊的问题其实早在这个环节就埋下了。第三个价值是作为设计和开发阶段的共同参照。后端工程师拿到DFD能快速定位自己负责的模块对应哪个过程前端工程师能从外部实体和数据流反推需要哪些页面和接口测试工程师则可以把每一条数据流当成一条测试线索顺着它梳理正向和逆向场景。可以说DFD画清楚了后面的功能拆分、接口定义、数据库设计都有了共同的起点。这也是为什么我一直坚持系统分析阶段不能跳过DFD哪怕时间再紧上下文图和零层图这两张图也一定要有。2. 分层建模从上下文图到最底层子图2.1 第一层上下文图先把系统当成一个黑盒DFD的分层建模思路我在项目里反复跟新人强调永远不要试图在一张图里画完整套系统否则这张图一定没人看。正确的做法是从上下文图开始把整个系统当成一个黑盒只画它和外部世界之间的数据往来。上下文图画起来非常简单中间画一个大方框代表待开发的系统方框里只放一个过程节点名字一般就叫某某系统。方框外面画上所有外部实体然后用数据流把它们和中间的方框连起来。比如做图书借阅系统外部实体就是读者和管理员从读者那里流入的是借阅申请和归还申请从系统流到读者那边的是借阅结果和归还结果管理员那边可能还有书目维护请求和逾期通知这类数据流。画完这张图你要能清楚地回答三个问题系统为谁服务系统从外面接收哪些数据系统向外面发送哪些数据如果这三个问题答不清楚说明需求还不完整这时候不应该急着往下分解。我用一个简化的文本示意来说明图书借阅系统的上下文图可以长这样读者 --(借阅申请)-- [ 图书借阅系统 ] 读者 --(归还申请)-- [ 图书借阅系统 ] [ 图书借阅系统 ] --(借阅结果)-- 读者 管理员 --(书目维护请求)-- [ 图书借阅系统 ] [ 图书借阅系统 ] --(逾期通知)-- 管理员注意看在上下文图阶段系统内部一个过程都没有展开数据存储也完全看不到。这个黑盒视角是为了让所有人先对齐边界不要一开始就陷入某个功能细节。2.2 零层图第一次打开系统黑盒上下文图确认无误之后第二步是把中间那个唯一的过程节点展开成几个主要过程这就是零层图。零层图也叫做系统内部第一层分解图它展示的是系统的主要功能域以及这些功能域之间、功能域与外部实体和数据存储之间的数据往来。还是用图书借阅系统举例。打开黑盒之后系统内部至少会有这几个过程读者管理、图书管理、借阅处理、归还处理、逾期通知。数据存储也能在这里第一次出现比如读者库、图书库、借阅流水库、逾期记录库。每个过程负责一组内聚的数据加工彼此之间通过数据流协作。零层图最需要把握的分寸是过程数量既不能太少也不能太多。太少说明拆得不够后续还得做大量解释太多说明你已经跳过了逻辑层次的过渡直接进入细节。我个人的习惯是控制在5到9个过程这是一个能被短期记忆轻松容纳的数量评审时大家盯着一张图看三分钟基本就能建立整体认知。读者 --(借阅申请)-- [借阅处理] 借阅处理 --(借阅记录)-- 借阅流水库 借阅处理 --(扣减库存)-- 图书库 借阅处理 --(借阅结果)-- 读者 管理员 --(书目维护请求)-- [图书管理] 图书管理 --(档案更新)-- 图书库 图书管理 --(维护结果)-- 管理员 [逾期通知] --(逾期记录查询)-- 借阅流水库 [逾期通知] --(逾期通知)-- 读者上面这段只是零层图的一部分实际的图里还会有归还处理、读者管理等过程但你已经能看到它和上下文图的区别一是系统边界内的过程从1个变成了多个二是出现了数据存储三是外部实体仍然保留在图上因为数据流必须能追踪到它的最终来源和去向。2.3 逐层分解和数据流平衡零层图之后每个过程如果内部还比较复杂可以继续向下分解形成一层图、二层图。这里有一个整套DFD方法论中最核心的规则叫作数据流平衡或者叫父子图平衡。规则表述起来并不复杂父图中某个过程节点的所有输入数据流和输出数据流必须在它的子图中完整出现。子图中可以增加数据存储和内部过程之间更细的数据流但对外界暴露的输入输出一个都不能少一个都不能多更不能改名字。这句话听起来像废话但实际执行时非常容易出错。因为画子图的时候人的注意力会集中在内部细节上很容易顺手把某个输出丢掉或者为了补逻辑在子图里画了一条父图完全没有的输入流。等到评审的时候懂行的人只要拿着父图和子图一对比立刻就会发现不平衡这时候其他所有细节都会显得不可信。我自己的检查习惯是每画完一张子图就单独列一个清单把父图里对应节点的输入输出全部抄下来再和子图最外圈的输入输出逐一对照。这个动作花不了三分钟却能挡掉评审会上最尴尬的时刻。2.4 借阅处理子图的平衡演示继续上面的案例零层图里有一个过程叫借阅处理父图中它的输入是借阅申请输出是借阅记录、扣减库存和借阅结果。展开成一层子图时四个边界数据流必须原样保留。假设借阅处理内部又分为三个过程校验读者资格、检查图书库存、登记借阅流水。那么子图的数据流可以这样组织读者 --(借阅申请)-- [校验读者资格] 校验读者资格 --(读者状态)-- [检查图书库存] 检查图书库存 --(库存扣减结果)-- [登记借阅流水] 登记借阅流水 --(借阅记录)-- 借阅流水库 检查图书库存 --(扣减库存)-- 图书库 登记借阅流水 --(借阅结果)-- 读者对照父图检查一下父图中的借阅申请在子图中出现了借阅记录、扣减库存、借阅结果也都出现了一条不少、一条没多这就是平衡。如果我在子图里多画了一条读者余额更新直接输出给读者而父图的借阅处理并没有这个输出那这条平衡就被打破了说明要么父图画漏了要么子图画多了无论如何都必须在评审前解决。3. 绘制DFD的实操步骤与命名规范3.1 从零开始的五步画法如果你手头只有一堆需求文档想从零画出一套完整的DFD我建议严格按照下面这个顺序走。这不是唯一的顺序但这是我踩过坑之后沉淀下来的相对稳妥的路径。第一步圈定外部实体。把所有需要和系统交换数据的角色、外部系统列出来。这一步不要想系统内部逻辑只问一句话数据是从哪里来的最终要到哪里去凡是答案里出现的人或系统就是外部实体。第二步整理核心过程。把需求文档中的功能点按数据加工的角度重新组织去掉那些纯页面交互的描述提取出真正在处理数据的动作。比如点击按钮查询订单这个描述里核心过程是查询订单点击按钮是界面控制跟DFD无关。第三步建立数据存储。把系统需要长期保存的数据找出来。判断标准也很简单如果某条数据流完成后数据还需要在将来被其他过程使用它就应该落到一个数据存储里。例如借书时必须记录借阅流水这个流水未来要被归还处理、逾期通知使用所以它必须成为存储。第四步连线并命名数据流。这一步是这个阶段最重要的产出。每条箭头上都要写清楚这条数据流里携带的业务数据是什么。注意前期你可以用便利贴或者在线白板先画草图不要一上来就纠结对不齐、颜色统一这些排版问题内容正确比美观重要得多。第五步做完整性检查。把上下文图、零层图、子图叠起来检查平衡关系逐条数据流核对是否有来源、有去向、有名字把所有不明确的地方记下来作为下一轮需求确认的问题清单。这套五步法放在团队里前两步最好由业务分析师和产品经理一起做后三步则需要把架构师和资深开发拉进来。DFD不是一个人的独角戏它是需求和设计之间的翻译器两拨人必须都参与翻译过程。3.2 命名和数据字典决定DFD能否落地的细节DFD如果只是画个大概那它只能算一张示意图真正让它变成工程模型的是配套的数据字典。数据字典的作用是给DFD里每一条数据流、每一个数据存储、每一个过程做严格的定义。命名规范是第一步。数据流的名字必须用名词或名词性短语因为它代表的是数据不是一个动作。你可以写订单信息支付回执库存变更结果但不要写检查订单发送支付。反过来过程节点的名字必须用动词开头的动宾短语因为过程代表的是加工动作比如校验订单计算运费生成日报表。思路很简单箭头上面应该是名词圆圈里应该是动词。数据字典要定义到字段级。一条借阅申请数据流在数据字典里要展开成读者编号 图书编号 借阅时间 申请批次号这样才算定义完成。同样的图书库这个存储也要列出它的核心字段图书编号、书名、库存总量、在库数量。这项工作确实烦琐但它是DFD和数据库设计之间最关键的桥梁。我见过很多团队DFD画完就扔后面的数据库设计完全是另起炉灶结果字段对不上、接口含义模糊根本原因就是在DFD阶段跳过了数据字典。3.3 过程拆到多细才算合适新手画DFD最常见的困惑是一个过程到底要拆到多细拆深了图变成蜘蛛网拆浅了又说不清逻辑。我常用的判断标准有三个。标准一是看过程的内聚性。如果一个过程里塞了多个业务意图比如校验并入库这种把两个意图并在一起的说明它应该拆开。校验是一件事入库是另一件事合并在一起后续无论是开发分工还是测试设计都很难处理。标准二是看过程之间是否需要交换数据。如果拆出的两个子过程之间没有任何数据流往来说明它们被拆碎了应该合并成一个。数据流的存在是两个过程之间确实存在协作关系的证据没有协作却被拆开只是徒增图形复杂度。标准三是考虑读者的认知负担。一个过程展开之后子图里的过程数量我会尽量控制在7个以内再配合数据存储和控制点一页纸能讲清楚。如果超过7个我会停下来反思是不是当前层次选错了或者上面一层应该多拆几个过程。要记住一个原则DFD是一个逐步细化的沟通工具不是最终程序的流程图。它的目标是在每个层次上都让读者在几分钟内看懂所以拆分的深度取决于听众而不是取决于程序代码的复杂程度。4. DFD如何衔接数据库设计与详细设计4.1 从数据存储到数据表我早期做项目的时候有过一个教训数据库设计已经做完了回头才发现DFD里一个存储对应的是两张表而有的表在DFD里完全没有数据来源。这个问题的本质是DFD和数据模型没有对齐而解决方案恰恰很简单就是让数据存储成为两者之间的锚点。DFD里的每个数据存储在设计阶段至少要对应到一张数据表或者一个集合。比如借阅流水库这个存储展开到数据库设计里大概率会变成一块以借阅记录为核心的表区域包括借书信息、还书时间、续借次数等。更为关键的是一条数据流是数据表的写入来源的证据。如果某张表没有任何数据流指向它这张表的数据从哪来这是一个值得在评审会上好好追一追的问题。我在项目里用过一个很土但非常有效的方法把DFD里的每个数据流标签和数据存储字段整理成Excel表一列写数据流名一列写字段组成一列写它对哪个存储做读或写操作。这张表做出来之后数据库设计几乎就是照抄作业。它还能反过来校验需求遗漏比如你发现某条数据流只在图上画了但没有任何过程会给它产生数据那这就是一个需求断链。4.2 DFD和用例图、流程图怎么分工一个完整的系统分析不能被DFD单独撑起来它还需要用例图和流程图配合。很多人搞不清这三者怎么分工我在评审会上会这样解释用例图回答的是什么人能做什么事。它表达的是角色与功能之间的关系比如读者可以借书、可以还书管理员可以维护书目。用例图关心的是功能权限和行为意图。流程图回答的是某件事按什么顺序执行以及在什么条件下走哪条分支。它是过程视角关心的是控制流、分支判断和活动顺序。DFD回答的则是处理某个业务的时候数据从哪里来经过什么加工存到哪里最后送到哪里。它是数据视角关心的是数据的输入、处理和输出。举个例子同样描述借书业务。用例图会画一个读者和一个借书用例连一根线流程图会画从接收申请、判断读者资格、检查库存到登记流水的一串顺序节点里面包含判断框DFD则会画出一条借阅申请的数据流进入借阅处理过程再从过程流向读者库、图书库和借阅流水库。这三张图放在一起从三个角度交叉验证需求文档才称得上完整。我见过不少团队只用用例图加原型就开始开发结果最隐蔽的数据问题到联调阶段才爆发改起来伤筋动骨而这种问题如果在DFD阶段花半天梳理完全是可避免的。4.3 评审DFD时要回答的三个问题DFD作为系统分析阶段的交付物最终要经得起评审。我在评审会上通常会引导大家围绕三个问题来问。第一个问题每一条数据流是否都有完整的生命周期也就是说任何一条数据流从它进入系统到它最终离开系统中间的过程是否合理衔接。如果一条数据流进入一个过程之后没有任何输出对应它这个过程的加工结果去哪了这是最常见的数据断链。第二个问题数据存储是否都被合理访问每个存储都要有写入它的数据流和读取它的数据流。只写不读的数据往往意味着功能缺失只读不写的数据则意味着数据来源没有定义。这两种情况都需要回到需求里确认。第三个问题外部实体的边界是否正确。外部实体有没有被误画进系统边界内外部实体之间有没有发生直接数据交换。严格来说外部实体之间不应该有数据流因为系统没有参与的数据交换不在系统分析范围内。如果你发现两个外部实体之间画了箭头说明系统边界又模糊了。这三个问题问完一套DFD是否合格、是否具备落到设计的条件基本就有结论了。5. 常见错误、排查经验与工具心得5.1 我见过最多的错误盘点我这些年看过的DFD不算少有团队内部画的也有外包交付的其中有四类错误出现频率最高几乎每次都能看到。第一类是把控制流混进数据流。最常见的就是点击提交选择日期这类箭头。这类箭头表达的是用户的界面操作不是业务数据本身画进来之后整个图里就会混入大量和数据处理无关的信息越改越乱。我的处理办法是遇到这类描述直接划掉并提醒作者DFD只画数据流操作交互请放到流程图中去表达。第二类是外部实体被画进系统边界里。这种情况往往发生在作者对系统边界本来就不确定的场景里。比如有的作者把管理员画成了系统内部角色理由是管理员要登录系统管理数据。但登录后做的事属于系统功能管理员本身仍然是系统外的角色他是数据的使用者不是数据加工过程本身。这个边界不搞清楚系统范围就会随之摇摆。第三类是数据流没有名字或者名字取了动词。一张DFD上如果出现三四条没有标签的箭头整张图的可用性会急剧下降。因为DFD的价值全都压在数据流携带什么数据这件事上名字缺失等于告诉读者这里的数据不确定。第四类是父子图不平衡。这是我前面反复提到的核心规则但在实际交付里依然经常破功。原因基本都是一层层往下画的时候只盯着当前层内部的逻辑细节忘记拿父图对照等画完发现多出来的或漏掉的数据流已经不太敢改了索性瞒着不报。我的建议是宁可早点暴露不平衡让团队一起判断是补哪边也不要带着问题去评审。5.2 一个真实的评审翻车现场有一次我做内部评审团队里一位同事展示他画的零层图整体画得相当完整过程节点规范、存储标签清晰。但评审过程中项目经理指着一个数据存储开口矩形问读者信息这个存储放在这里意思是不是读者数据要存在我们系统里我们目标架构里读者资料是放在上游统一档案系统里的。这位同事当场有点慌因为他确实没有确认过读者信息是本地存储还是远程调用。DFD的数据存储表达的是数据停留地点如果这里画成本地存储等于架构上做出了一个有点关键的决策而他自己并没有意识到。后来我们回到需求阶段确认读者基础资料由统一的档案中心管理本地系统只保存读者在借阅场景下的扩展信息于是DFD被改成了两个存储一个外部存储一个本地存储图的语义立刻准确了。这个案例给我留下了很深的印象。DFD的价值不只是画数据流它还会暴露你对系统边界、数据归属的理解而这种理解如果不画图很可能会一直藏在脑子里不被发现直到编码阶段才变成返工。5.3 排查速查表下面这张表我经常在带新人时打印出来贴在工位旁边。每画完一层DFD对着表过一遍大部分常见问题都能自己发现检查项通过标准常见失败表现数据流命名名词性短语描述数据内容写成了动词或没写过程命名动宾结构描述加工动作只写名词成了存储名外部实体位置全部位于系统边界之外把角色画进系统框内父图子图平衡输入输出一一对应子图多流或缺流外部实体交互外部实体之间无直接连线客户直接连银行存储读写每个存储都有来源和去向有读无写或有写无读数据字典覆盖关键数据流有字段定义只有名字没有结构单图复杂度主要过程在7个以内节点多到无法阅读如果你发现自己某一条对不上不要急着改箭头先回到需求里去确认业务规则因为DFD里的表达问题背后往往是需求理解不到位。5.4 工具选择和团队协作习惯最后聊一下画图工具。DFD不需要特别专业的建模软件普通的绘图工具、在线白板、开源画图工具都可以胜任。我自己的习惯是第一版草图阶段用在线白板因为要快速改、频繁讨论白板的易用性比排版美观更重要。等草稿被评审确认了再到绘图工具里整理成最终版导出图片放进需求文档。这里有一个技巧即使工具可以自动连线我仍然建议手动调整箭头的走向确保每个标签都放在不会被线穿过的地方。标签和图线重叠是DFD可读性的隐形杀手一张图画完如果标签都叠在一起就算内容正确评审时也容易漏看。团队协作层面最大的坑是版本不一致。有人更新了上下文图但没同步零层图结果评审时同一套系统两处边界描述不一致讨论了半天才发现是版本问题。我的办法是在文档里放一个DFD配置页把当前版本号、修改日期、维护人列清楚每次改动顺手更新配置页。这个习惯花三十秒但能省掉评审中双倍时间的混乱。画了几年DFD之后我最大的体会是它最值钱的地方不是让你交出一张漂亮的图而是逼着你用数据的视角把系统重新想一遍。每次我发现自己画不下去或者上下文图里两个外部实体之间出现了莫名其妙的连线那基本意味着需求里还有没讲明白的地方。最后留一个小习惯给你画完分层DFD拿一支笔从外部实体出发沿着数据流箭头把每条路径走一遍直到另一个外部实体结束。如果半路断了或者某条数据流没有来源就直接出现在存储里恭喜你提前找到了一个需求漏洞。这套检查用不了十分钟但省下来的返工时间够你再画三张图。