数据流图DFD实战:分层建模、缺陷诊断与数据字典
发布时间:2026/9/8 9:57:32 作者:尧图编辑部 阅读量:1,286

做了小十年系统分析和架构梳理我越来越觉得数据流图Data Flow DiagramDFD是被很多人低估的工具。前阵子接了一个老系统资产盘点项目几千个功能点散在代码里没人说得清数据从哪来、经过谁、存到哪、最后交给谁。我花了两周时间用DFD把整个系统从顶层上下文图一层层分解到底层加工说明居然把混乱的现状摸得清清楚楚。这件事让我确定DFD不只是结构化系统分析与设计的教科书考点它是一套真正能落地的信息流动建模方法。这篇就围绕DFD的分层画法、父子图平衡、黑洞/奇迹/灰洞三类缺陷以及配套的数据字典和实操清单展开适合正在做系统分析、需求梳理、老系统重构或者准备软件工程考试的同学参考。1. 先把DFD放在它该在的位置建模逻辑与四种基本元素1.1 DFD的核心逻辑系统本质是一台数据处理器很多人第一次接触DFD就被符号规则搞晕其实DFD的思想非常朴素无论系统多大、多复杂本质就是输入数据加工数据输出数据必要时暂存数据。DFD把整个系统抽象成一个数据处理网络只关心信息流动的方向和变换不关心谁来执行、什么时候执行、用什么技术执行。这也是它适合做早期分析和需求沟通的原因——业务人员和技术人员都能看懂。举个例子一个订单系统在DFD里呈现的不是页面、按钮、数据库表而是这样一条链路客户外部实体产生订单信息数据流进入处理订单加工加工读取库存表数据存储最终输出订单确认给客户同时写入订单记录存储。这条链路剔除了所有与信息流动无关的杂质读者一眼就能看出系统到底做了什么。1.2 四个基本符号不多不少恰好够用完整的DFD只需要四类符号外部实体、加工、数据流、数据存储。它们之间的关系可以简单记忆为外部实体产生或接收数据加工负责变换数据数据流是数据在要素之间的移动通道数据存储是静止等待的数据集合。下面是两个主流建模流派对符号的约定差异模型元素Yourdon/DeMarco 风格Gane/Sarson 风格外部实体矩形圆角矩形带阴影的方形加工圆形或圆角矩形竖向圆角矩形数据流带箭头的直线带箭头的直线数据存储双横线开口矩形国内教材多用Yourdon风格但实际工作中用什么风格不重要关键是团队约定一致。我自己习惯用Gane/Sarson风格画外部实体因为它更接近人的直觉形象评审时业务方不容易把外部实体和加工混淆。关于加工需要特别强调一点加工必须有编号。顶层上下文图里那个代表整个系统的加工通常编号为0下一层分解出的加工编号为1、2、3……再下一层则编号为1.1、1.2、2.1等。编号不只是为了好看它承担着父子图对应关系和最终模块划分的重要索引功能。没有编号的DFD在层级多起来之后根本无法追溯。1.3 DFD和流程图、用例图的边界感很多初学者会把DFD和流程图混为一谈实则是两套完全不同的模型。流程图关注控制流强调先做什么后做什么里面的箭头代表程序执行顺序菱形代表判断分支DFD的箭头则只表示数据流动方向不表达时间和顺序。一个DFD里画判断分支反而会破坏它的数据流视角。用例图则关注目标视角用户希望通过系统达成什么目标。它不回答订单数据如何一步步变成发货单这类问题。DFD关注的是处理视角数据如何被输入、加工、存储、输出。实践中我遇到过不少项目团队用用例图粗略梳理完业务就开始设计数据库结果漏掉了大量中间数据变换环节。合理做法是先用用例图界定功能范围再对核心业务画出DFD做数据流动分析。前者解决系统为谁服务的问题后者解决系统内部怎么处理信息的问题。2. 动手第一步从上下文图开始把系统边界钉死2.1 上下文图画法一个加工就是整个世界DFD的绘制强烈建议从顶层上下文图开始。上下文图只有唯一一个加工编号为0代表整个系统外部实体与这个加工直接相连所有数据流都是跨系统边界的信息交互。它的价值在于强制分析人员回答两个问题系统的用户和外部系统是谁系统对外接收什么、输出什么我实际的画法分三步走。第一步列出系统所有外部参与者包括人如客户、管理员、组织如供应商、外部系统如支付网关、ERP。第二步针对每个外部参与者逐一列出它提供给系统的信息和它期望从系统获得的输出。这里要特别注意一个外部参与者往往有多条流比如客户既提交订单信息也接收订单确认还可能接收促销消息。第三步把所有输入输出流画在系统加工周围标注名称形成一张星型图。下面是一个简化版的上下文图描述示意类似记账系统┌─────────────────────────┐ 客户 ─────► │ ─────► 记账系统 │ ◄───── (加工 0) │ ───── │ ─────► │ └─────────────────────────┘实际画图时不会这样画文字不过思路就是这样外部实体在四周数据流穿过边界指向或离开中央的0号加工。这张图应尽量保持简单一般不要超过5~9条主数据流如果主数据流过多说明系统边界可能划得过宽或外部实体颗粒度太粗。2.2 边界之争到底哪些流程该画进系统上下文图最难的不是画符号而是确认边界。同一个业务流程不同人理解下的系统边界可以完全不同。我接过一个支付结算项目业务方坚持把人工审核退款画进系统内部但实际上这项工作由运营人员在外部手工完成系统只提供一个退款工单。如果把人工审核画进系统内部上下文图没错但后续做DFD分解时会被迫把运营决策也建模成加工而它根本没有自动化的数据变换逻辑最后只能做出一个无法落地的虚假功能。判断边界有一个简单法则看数据变换是否由系统负责。如果某个动作靠人脑或线下工具完成只与系统交换数据那它就是外部实体的一部分。如果动作由系统模块自动完成那它才是加工。另一条经验是上下文图中的所有数据流都必须是系统真正输入或输出的物理载体虚构的、计划中的功能不要画进现状图。画现状DFD就忠实画现状画目标DFD就画目标两者混淆会导致整个分析失真。2.3 上下文图完成后不要急着往下分解很多人画完上下文图就开始逐层分解忽略了评审环节。其实上下文图是整个DFD分层的根根错了后面全错。每画完一版建议至少做一次业务方确认逐条核对每一条数据流的业务真实性。可以问业务方您在实际操作中是直接把excel表传给系统还是系统从某个平台自动拉取这类问题能暴露大量信息流动的真实细节。还有一点容易被忽略上下文图上的加工数量恒等于1。如果出现两个加工说明已经不在上下文层了。这是检查DFD层级关系的第一步也是新人最容易犯的错。3. 分层分解的尺度父子图平衡怎么理解、怎么查3.1 从0号图到子图编号就是导航地图上下文图完成后要对0号加工做进一步分解生成1号图也叫0层图。1号图内部会有多个加工每个加工都可以继续分解成更小的一张子图从而形成一棵完整的分解树。编号规则必须严格父图加工编号为N其子图内的加工编号为N.1、N.2、N.3……如果N.2又分解出子图那层加工编号为N.2.1、N.2.2……以此类推。这个规则相当于给每张子图一个精确的坐标。同事之间讨论时可以直接说1.2号加工内部逻辑不太对对方立刻定位到完全相同的节点。我在一个信贷项目中遇到几十张DFD分图全靠编号体系维护追溯关系否则早乱了。需要注意数据存储也经常需要编号但不同风格做法不一。Yourdon风格中数据存储用D1、D2编号Gane/Sarson风格也常用D加数字。加工编号主要用数字层级不要和数据存储混用。3.2 父子图平衡的铁律外部接口必须完全一致这是DFD模型最核心也最容易被违反的规则。所谓平衡是指父图中某个加工的全部输入输出数据流必须与该加工对应的子图的全部对外数据流完全一致——数量一致、方向一致、名称一致。子图内部可以增加数据存储和中间数据流但跨子图边界的数据流一张都不能多、一张都不能少。为什么必须这样因为在分层架构里每一层都是上一层的抽象替代。父图告诉你订单处理接收订单信息、输出确认单子图则展示订单处理内部的详细步骤但步骤再多它对外的接口也必须与父图一样。如果子图突然多出一个发货单输出就意味着父图的信息不完整如果少了一条库存不足通知则意味着子图丢掉了父图承诺的功能。任何不平衡都会导致系统设计时出现接口缺口或多余逻辑。做平衡检查时我习惯用一个清单对照父图中该加工的所有输入流是否全部出现在子图边界父图中该加工的所有输出流是否全部出现在子图边界子图边界的数据流名称和方向是否与父图完全一致子图边界是否出现了父图中不存在的数据流数据存储不参与父子平衡判断子图内部可以自由读取父图中未显示的存储。3.3 一个真实父子图不平衡案例订单处理子图多画了一条付款流这是一个我在培训中经常使用的例子。某系统的父图0层里处理订单加工编号2有两条输入流客户订单、库存信息两条输出流订单确认、缺货通知。但在它的子图2中分析人员额外画了一条输入流客户付款信息直接从外部实体客户流入子图内的校验付款加工。初看之下似乎无伤大雅但严格对照父图子图边界多出了客户付款信息这条父亲图上没有的输入流这就是典型的父子图不平衡。潜在后果是什么后续模块划分时会突然冒出一个不在父图规划中的收款处理功能导致系统范围膨胀或者实现时发现父图根本没定义支付相关流程整个功能无处安放。排查过程其实不复杂。先把父图加工2的全部输入输出列到左边再把子图2的全部边界数据流列到右边逐一勾对。上例两列差异立刻显现。修复要回到业务本质支付功能到底属不属于处理订单如果属于父图必须加上这条输入流如果不属于子图里就不能出现。这个例子是想说明平衡检查不是机械流程它逼你去重新思考功能边界的合理性。3.4 分解层级怎么控制什么时候停DFD分层不是越深越好。分解的目标是让每个加工保持功能内聚且小到可以用一段简短文字描述逻辑。业界习惯用每个加工做的事应该在几行字以内能说清来检验是否可以停止分解。如果加工涉及复杂的多条件判断则继续分解或写加工说明如果加工只做一次简单计算如计算订单总额就可以停了。同时建议单张子图内的加工数量控制在5到9个对应人脑的组块记忆上限。超过9个说明分解粒度有问题要么存在可以合并的加工要么本应拆成两张子图。我在画大型系统DFD时往往控制在每张图不超过7个加工方便一轮评审就能看完整。过度分层会让文档膨胀到难以维护也偏离了DFD作为沟通工具的本意。4. 黑洞、奇迹与灰洞三类建模缺陷的诊断与修复4.1 三类缺陷的定义一张表讲清楚DFD建模质量的最大敌人不是画错符号而是出现三种语义缺陷黑洞、奇迹和灰洞。它们描述的是加工与数据流之间的逻辑矛盾。缺陷类型表现通俗理解黑洞加工只有输入流没有输出流数据吃进去就消失了奇迹加工只有输出流没有输入流数据凭空产生灰洞加工既有输入也有输出但输入的信息不足以生成输出有原料但原料凑不出成品这三种缺陷再进一步可理解为逻辑上的不守恒。信息既不能凭空产生也不能凭空消失更重要的是信息变换必须有充分的输入信息来支撑输出结果。灰洞往往比黑洞和奇迹更隐蔽因为数据流数量是平衡的乍一看没问题仔细分析才知道输出所需的某个字段在输入里根本不存在。4.2 黑洞是怎么产生的只管接收不管后续我见过最典型的黑洞出现在数据上报类加工里。分析人员画了一个加工叫接收运维数据输入流赫然列着服务器日志但加工没有任何输出。实际情况可能是接收后的数据要经过清洗、存库、告警判断等多个环节但分析时尚未想清楚于是干脆先画一个接收动作把数据放进去就悬在那里。排查黑洞时对每个加工问一句话你处理完的数据要流向哪里如果答不上来多半是流程没画完整。修复路径是补充缺失的输出流或者如果这个加工真的只是暂存那应该增加一个数据存储输出而不是让数据流消失。4.3 奇迹是怎么产生的默认系统内部什么都知道奇迹型加工通常发生在汇总/统计类节点。例如加工生成统计报表只有输出流月度汇总表没有输入流。分析人员心里默认数据都在数据库里报表自然能出但DFD要求把所有信息输入显式画出来。该加工至少应该有输入流原始流水数据甚至还应从某数据存储读取历史数据。奇迹的另一种来源是愿景式建模。把智能推荐画成一个加工直接输出推荐列表但没有任何输入。这个加工的真实输入至少包括用户行为数据和商品信息。智能只是加工内部算法不是无源之水。修复方法是回到业务梳理清楚加工到底需要哪些外部输入、读取哪些存储把数据流补齐。4.4 最隐蔽的灰洞输入信息不足以支撑输出灰洞是我在评审中遇到率最高的问题因为信息量的匹配需要结合业务知识判断。举个例子某加工确定信用额度输入流为客户身份信息输出流为信用额度。表面上既有输入又有输出不违反守恒定律业务上却说不通。仅凭身份信息怎么可能算出额度还需要历史还款记录、收入证明、征信报告甚至内部风控策略。缺少这些加工就是灰洞——数据进去加工却变不出合理的结果。灰洞的判定方法是对每个加工把输出数据流拆成最小数据项逐个检查这些数据项能否从输入数据流中直接或间接推导出来。无法推导的那部分必须补充输入流。灰洞修复的难点在于它不是纯粹的模型问题而是业务逻辑不完整的结果往往需要和业务方重新确认加工规则。这时候数据字典就派上用场了。4.5 用表格做缺陷扫描比肉眼高效得多实际项目里面对几十张DFD靠肉眼逐张看效率太低。我有一个习惯建立一张缺陷扫描表逐个加工登记输入流、输出流然后进行检查。加工编号加工名称输入流输出流缺陷判断说明1.1校验订单订单信息有效订单无输出可推导1.2计算金额订单明细订单金额灰洞缺少单价输入1.3扣减库存商品编号、数量扣减结果黑洞无后续输出这张表做完问题一目了然。然后再回到DFD上修改。用这种方式做全量检查半小时能把数十张图的缺陷筛完。5. 数据和逻辑双轨跟进数据字典与加工说明的配合5.1 数据字典到底是什么只有图形没有定义DFD的表达是模糊的。订单信息这四个字在不同人眼里可能是完全不同的内容。数据字典就是为了消除这种歧义它为每一个数据流、数据存储、数据项建立明确定义。它和DFD的关系类似代码注释与代码的关系——没有注释的代码能运行但难维护没有数据字典的DFD能看懂但难传递。数据流条目一般使用组合符号描述数据结构。我习惯用下面这些符号 表示由……组成表示并且[] 表示选择其中之一{} 表示重复若干次() 表示可选表示注释例如客户订单的数据字典可写为客户订单 订单号 客户编号 订单日期 {商品编号 数量 单价} 订单总额这个条目清楚定义了订单的构成DFD中任何叫客户订单的数据流都以此为基准。灰洞检测时只要把输出流的字段全列出来再对照输入流的字段缺少的部分立刻暴露。5.2 数据流字典、数据存储字典、数据项字典在较大项目里数据字典往往分三类维护。数据流字典描述流动中的数据结构数据存储字典描述入库后的数据集合数据项字典描述最小数据单位包括类型、取值范围等。一份成熟的数据项字典能提高多方协作效率避免同一个字段在不同文档里出现三种叫法。数据存储也需要定义结构因为它本质上就是静止的数据流。例如库存表的条目可以写作库存表 {商品编号 商品名称 规格 当前数量 安全库存量}这三类数据字典必须和DFD保持一致。改图不改字典或者改字典不改图都会造成文档漂移。我一般要求团队在评审DFD时同步评审字典把两者当作一个整体。5.3 加工说明用结构化语言把变换逻辑写清楚DFD上的加工只是一个圆圈加编号它内部的决策规则必须借助加工说明才能表达。加工说明常用的三种工具是结构化语言、判定表、判定树。结构化语言与编程语言不同它只允许顺序、选择、循环三种结构采用自然语言写成。例如加工1.2 计算订单金额 IF 会员等级 普通会员 THEN 订单金额 商品金额总和 ELSE 订单金额 商品金额总和 × 0.9 输出订单金额这种描述既没有技术细节又足够精确业务方也能看懂。当条件组合复杂时判定表比结构化语言更直观。比如运费计算依赖重量和配送区域两个条件用判定表列出所有组合不易遗漏。当条件存在优先级时判定树更合适。三者可以混合使用核心原则是让加工规则无歧义。不要指望在DFD中直接画出每个加工的实现细节那是流程图的工作。加工说明补充的是业务规则不是程序逻辑。有了清晰的加工说明后续开发人员才能知道该加工要实现什么行为。5.4 图、字典、说明三者的迭代顺序实际操作中我不建议一次性把DFD画到完美再补字典。我的节奏是先画出上下文图同步建立顶层数据字典再逐层分解每分解一层补充该层引入的新数据流和数据存储条目最后集中写加工说明。三者在迭代中逐步逼近精确。如果后期发现加工说明需要某个输入字段而DFG上没有对应数据流那就回头修改DFD和字典。这是一个反复校准的过程。6. 画图实操工具选择、命名规范与DFD评审清单6.1 工具怎么选从白板到专业建模工具DFD本质上是一种结构化图形很多工具都能画。关键是区分使用场景场景推荐工具理由早期需求探讨白板/手绘快速、随意、鼓励修改中小项目落文档draw.iodiagrams.net免费、支持分层、便于协作共享与代码仓库共存PlantUML文本化建模可用文本描述DFD支持版本管理便于diff专业软件工程文档StarUML、Enterprise Architect内置多种图形规范支持模型驱动个人更推荐中小团队用PlantUML维护长期DFD文档。它对画图工具依赖低文本描述与代码风格类似可以放进Git仓库随代码一起评审。但PlantUML的DFD支持需要引入额外依赖配置成本略高。如果团队没有稳定性要求直接draw.io也够用。Visio功能全面但偏重协作性不如在线工具。6.2 命名规范让每一个数据流都名副其实画DFD最容易被忽视的就是命名质量。加工名称必须使用动宾短语如验证登录信息生成发货单更新库存数量这样能清晰表达加工行为名词性短语如订单处理库存管理往往过于笼统无法区分加工的具体职责。数据流名称必须是名词性短语如客户订单库存数量缺货通知不要出现数据信息内容这类空泛词。数据存储命名要与加工命名风格区分通常用具体的数据集合名如订单表商品清单用户档案。外部实体最好使用真实业务角色名如客户供应商库管员避免用用户这样模糊的统称。命名统一后评审沟通会顺畅很多。我在评审时经常发现同一数据流在一张图叫订单在另一张图叫订单信息最后被判定为不平衡其实只是命名不一致。6.3 数据流数量控制与可视化整洁度一张DFD如果线条交叉成蜘蛛网几乎不可能评审。控制每条数据流的数量必要时要重新考虑加工边界。其中一条经验是一个加工的直接输入输出流总数尽量控制在5条以内。如果超过这个量加工能做的业务动作太杂应当继续拆分。另外一个实用技巧是合理布置数据存储。数据存储可以重复绘制在同一张图的不同位置只要编号一致即可不必因为一个存储被多个加工使用就连一堆交叉线。外部实体同样可以多次出现比如客户既在左上角又在右下角只要标注同一实体名就表示同一个对象。这个约定能大幅降低图的复杂度。6.4 评审检查清单每一步都对应一个坑最后分享一份我累积的DFD评审清单适用每张图的检查是否有至少一个加工上下文图中是不是只有0号加工加工是否有唯一编号编号层级是否与子图对应每个加工是否有输入流和输出流是否存在黑洞、奇迹、灰洞父图与子图边界数据流是否完全一致方向是否一致名称是否一致数据流是否存在无名缺失是否都是名词性短语加工名称是否动宾结构是否存在多动词加工数据存储是否编号是否存在没有数据流访问的孤立存储数据字典是否覆盖了所有命名数据流和数据存储是否与数据流组成一致数据是否有必要的业务人员确认这张清单在我参与的项目里基本每轮评审必用。它能帮助团队把DFD画得工整、平衡、可执行也能让新人快速上手建模。说来也怪越做系统设计越发觉得DFD这种看似朴素的建模工具才是从业务到实现的桥梁。它逼着我把每个数据流都起好名、把每个加工都问清逻辑、把每张子图都对平衡过程中暴露的问题往往比想象中多得多。如果你正在面对一个信息流混乱的系统别急着进技术设计先按这套方法把DFD画出来你会看到数据从哪来、到哪去、怎么变清晰呈现后的那种透彻感。