业务流程建模的一种实践方法
你是否有也有这些痛点
假设需求是这样
-
Story A:老师可以给学生布置作业,作业包含以下信息:
- 作业开启时间
- 作业截止时间
- 题目(多道)
-
Story B:学生可以对作业进行作答,作业具有以下几种状态:
- 「未开启」:提前布置,未到作业开启时间
- 「已开启」:已到作业开启时间,但学生还未作答
- 「进行中」:学生正在作答但未完成
- 「已完成」:学生已完成全部的作业
- 「已超期」:学生未在截止时间之前完成
-
Story C:
- 学生在「作业截止时间」之前可以作答且可以修改作答的结果
- 学生在「作业截止时间」之后仍可以作答,但无法修改已作答的结果,完成后仍然算「已完成」
-
Story D:
-
老师在「作业开启时间」之前,可以修改「作业开启时间」「作业截止时间」
- 存在规则:当前时间 >= 作业开启时间 > 作业截止时间
-
老师在「作业开启时间」之后,可以修改「作业截止时间」
- 存在规则:作业开启时间 > 当前时间 > 作业截止时间
-
场景一
开发中,发现产品逻辑不闭环
RD:「已完成」和「已超期」这两个状态好像有点矛盾(我想想怎么描述一下...)
PM:怎么矛盾呢,可以具体讲讲么?
RD:你看啊,Story C 中说 “截止时间之后仍然可以提交,但也算「已完成」”,但 Story B 中又说了,“学生未在截止时间之前完成,是「已超期」”,那这种截止时间之后的提交,到底显示「已超期」还是「已完成」呢。
PM:这种情况算「已完成」
RD:OK
RD:(半小时之后...)我又发现一个问题,假如超期都显示「已完成」,老师是不是就区分不出是超期完成的还是按时完成的了?
PM:啊有道理,那这里应该显示「已超期完成」代表已超期其已完成,「已完成」代表按时完成。
RD:心口有点乱🤦♂️,让我理一理,(心理活动:感觉这里应该有两个状态,一个和时间有关,一个和学生做作业的进度有关,如果要算成一个状态,我应该怎么做呢...)
场景二
UAT 时,发现对产品逻辑的理解存在不一致
PM:我发现这里有个 Case 不太符合预期,这个作业为啥不能修改「作业截止时间」了
RD:稍等我查查哈(进入到测试环境复现问题)
RD:(10分钟之后...) 不对呀,这个作业已经过了截止时间了,都已经截止了就不能修改了呀
PM:可以修改的,老师在截止后如果发现交作业的同学太少,他是可以再推迟截止时间的
RD:OMG... 你看在 Story D 里也没写呀,只写了「作业开启时间」之后可以修改
PM:作业截止之后也是「作业开启时间」之后呀,Story D 是包含了这个规则的
RD:是我想当然了,我改一下吧,你也在 PRD 里明确一下哈(心在流血,又要加班了)
PM:好嘞,辛苦了老铁(感觉我们沟通出了问题,这种情况怎么能在前期就避免呢)
RD:(1小时之后...)(心理活动:啊对了,如果截止时间推迟的话,「已截止」这个状态也得改,这种情况咋弄,还得找 PM 对一对...)
场景三
灰度上线后,发现没考虑到系统中的某些变化,对新需求的影响
QA:我发现有个场景在 PRD 里没写清楚,如果老师转班了会咋样?
PM:ORZ... 确实没有考虑到,RD小哥你这块现在是怎么处理的呢?
RD:我现在没处理呀,即使老师转班了,作业还是他的,新来的老师也看不到历史布置的作业
PM:感觉可能不太对,如果老师被别人替换了,那作业应该移交到新的老师
RD:你这个是新需求呀,能不能上线之后再迭代
PM:这个如果上线可能会有比较多 Bad Case,我提个新需求,能不能高优迭代一版,不然这个需求没法上呀(心理活动:我只负责作业这块的需求呀,之前完全想不到这茬,没办法,提个新需求吧...)
RD:也只能这样了,你赶紧提需求吧(心理活动:虽然很无语,但以后怎么才能在开发前就发现这种情况呢)
场景四
新需求,发现新需求改动成本太高
PM:Hi,老铁上次的需求太给力,这次我们迭代一版新需求哈
RD:啥子需求呢
PM:要新增一个角色:「助教老师」,助教老师拥有正教老师所有的作业权限,也可以对作业进行管理,但不能布置
RD:这个有点难啊,工作量有点大,要支持这个可能得先把系统重构一下...
PM:(心理活动:听到重构这两个词就害怕),可以说说为啥不,我感觉不是很难呀
RD:现在作业都是挂在老师上的,如果多个助教,没地方挂了呀,原来老师和作业是一对多,现在变成多对多了(心理活动:我太难了,刚上线就要改)
PM:要不先评估一下工时哈,最好能尽快上线
RD:我们先评估一下吧(心理活动:感觉这里面可能少了一个表,代表了班级,作业应该挂到班级上,如果 PM 提前能提供这些信息就好了)
总结一下
不知道大家是否有同感,以上的几个场景,在我们中间可能每天都在发生,但是问题到底出在哪儿呢?
- 是 RD 的不给力,不能充分留出扩展性吗?好像不全是
- 是 PM 没想清楚,导致需求总是遗漏逻辑吗?好像也不全是
总结下来,问题似乎是这几类:
- RD 与 PM 沟通语言的不一致,导致沟通时信息存在 Gap,这些 Gap 需要进行额外的沟通,才能达成共识,产生结论。
- 在进行一个需求时,会遗漏掉对现有系统的影响 ,除非是一些有充足上下文的“老员工”,否则 RD、PM 都可能会遗漏掉,导致后期需要返工。
- 新增一个需求,对系统产生了非常大的冲击,需要比较大的改动才能支持,后悔当时为什么没有留出扩展性,但是考虑太多扩展性又会陷入过度设计的怪圈,两难境地。
再收敛一下,问题的根本其实在于:
如何识别「业务本质」,并用一种 RD、PM 都能听得懂的语言,表达出来
领域建模的意义
识别业务
- 识别在 PRD 背后的「业务不变性」,即不管系统怎么设计,现实中的业务到底是什么样的
- 依据「业务不变性」设计的方案(产品方案 & 技术方案),会更符合用户需求、也会更稳定
理清影响
在出技术方案之前,先理清业务逻辑中隐含的因果关系,哪些事情会互相影响(又名:联动)
- 当前需求中哪些「事件」,会互相影响,如:「面试已创建」会导致「面试通知已发送」
- 当前需求中哪些「事件」,会影响现有系统,如:「面试已创建」会导致「投递阶段已转移」
- 系统中已存在的「事件」,会导致当前需求功能受影响,如:「员工已离职」会导致「面试官已变更」
对齐认知
- 成为 RD 与 PM 之间的沟通语言,避免沟通中的 Gap,进而提升业务交付效率
- 多方对齐认知,在解决问题之前,先识别清楚问题
建模的三种场景
初阶:在 PRD 产出之后
参与人员:RD
设计技术方案之前,对 PRD 中涉及到的业务进行领域建模:
- 帮助自己理清业务逻辑中的因果关系,明确要解决哪些问题
- 发现 PRD 中未描述清楚 / 逻辑不自洽的部分,与 PM 同学进行对齐
中阶:在 PRD 产出之前
参与人员:RD & PM
在 PRD 产出之前,PM & RD 共同进行领域建模,相比第一种方法,这种方法能更早的识别「业务不变性」,同时产研进行对齐,共同产出高质量的 PRD,减少后续的沟通成本。
高阶:DDD 战略设计
参与人员:RD、PM、业务人员、领域专家
通过领域建模,并对问题域的划分,进而进行微服务的划分,达成业务自治。这部分不是本文重点,不赘述。
建模过程
基于「事件风暴」的领域建模
EventStorming 是一种灵活的研讨会形式,用于协作探索复杂的业务领域 — Alberto Brandolini
事件风暴通过工作坊的形式,由PM、RD、业务各方共同参与的, 发现并对齐领域知识。
比较传统的做法是找一面大的白版,使用各种颜色的便签纸填写相关内容,往白板上粘贴。
现在也有一些更便捷的支持协作的在线工具可供使用:miro.com/
基本元素
使用不同形状与颜色的贴纸,作为领域建模的基本元素
基本流程
事件风暴
业务中客观存在哪些客观事实
- 这个阶段,参与者需要尽可能的罗列业务中可能发生的「事件」,使用橙色方形便签表示。
- 「事件」使用动词的过去式,比如:用户已创建(UserCreated),职位已停用(JobDisabled)。
- 罗列完后整体进行一遍整理,去除重复与不合适的事件,并所有人达成共识
命令风暴
业务中的事件是如何发生的,以及事件发生的因果关系是什么
-
事件不会凭空发生,这个阶段我们会针对事件如何发生,补充上更多的卡片:
- 「角色」「命令」:代表某个事件由什么角色通过什么命令触发
- 「外部系统」:某个事件由外部系统触发
- 「定时器」:某个事件因到达某个时刻而触发
- 「策略」:事件的发生依赖哪些规则或者条件,依赖了哪些业务规则
-
事件之间也有可能存在因果关系,换言之事件可能会触发导致另一个事件的发生,这种情况使用单向箭头将事件连接起来:
- 「实线箭头」:强一致,表示事件会同时发生
- 「虚线箭头」:弱一致(最终一致),表示允许一段时间后再发生
寻找聚合
业务的一致性边界
-
这个阶段需要对事件进行归纳为「聚合」
-
识别聚合的一些原则:
- 聚合具有业务的一致性边界,在整个生命周期内,任何业务变化都应该遵守定义的全部「业务规则」,比如:「作答记录」、「作答单题记录」同属于「作答记录」聚合,当对某道题进行作答时,整个作用作答记录的状态也需要相应的发生变化
- 设计小聚合(大聚合会限制系统的性能和伸缩性)
用例分析
角色基于什么信息做的决定,事实会加工成哪些观点
-
这个阶段会引入读模型,读模型可以认为有两种(本质上是一种):
- 「视图」:不同的角色会基于什么视图,来做决定,比如学生会基于「作答视图」来决定「对题目作答」
- 「观点」:由事实加工后的信息,比如:「作答已完成」,会导致「作答显示状态」发生变化。
-
事件对读模型的影响,同样存在强/弱一致的关系,同样使用实/虚线箭头表示。
领域模型
实体间的关系,以及聚合中包含的根实体、实体、值对象、属性
图例中,绿色:根实体、蓝色:实体、粉色:值对象、黄色:属性
状态机
业务生命周期的完整体现
关键点
识别业务的本质
-
从「业务」的角度而不是「系统」的角度思考问题,思考业务的本质是什么。用户不通过信息化系统,是如何完成业务中的活动的。
-
时刻反问自己,我列的这个事件到底是「问题」,还是「解决方案」,事件应该是对问题的描述,而解决方案可以有很多种,关键是在于对「问题」的识别。
-
举两个例子:
- 「笔试结果短信已发送」,反复推敲后我们会发现,“笔试结果短信”这件事本身就是个解决方案,背后的问题是:怎样能将笔试的结果送达用户,送达的介质可以是短信、邮件、电话、公告栏中的任何一种,所以「笔试结果已通知」才是问题本身。
- 「简历去重记录已创建」,这个场景下业务的本质实际上是在说,求职者在某些场景下不能重复进行投递,去重记录只是针对这个规则的一个解决方案,我们也可以使用其他方案来解决这个问题,所以它就不应该是事件。
对齐并达成共识
- 建模的结论(用例分析、状态机、领域模型)需要和 RD、PM 一起达成共识,这一点非常关键,如果对问题的识别存在分歧,那基于问题的解决方案(产品方案、技术方案)一定会存在问题,后期在开发阶段再调整的成本就会增加,也会减少因对需求认知不一致导致的需求变更,最终提高交付效率。
聚合的状态机是核心
- 状态机是对业务不变性的归纳概括,对状态机的识别很关键,确保聚合的状态流转是闭环的,并且与各方达成一致。尤其注意那些看起来是状态,但其实不是状态的东西,比如:「任务待开始」「任务已开始」「任务已取消」「任务已完成」「任务已超期」,其中「任务已超期」不是状态,它只是对超过预计完成时间的描述,而任务的状态描述的是任务是否在进行,待开始/已开始的任务都存在超期的可能,但超期不代表任务不进行了。
常见疑问
Q:为什么识别业务本质这么重要,识别 PRD 不行么?
A:因为业务存在不变性,比如「面试」这件事,无论是:视频/现场面试、电子/纸质简历、通过系统/EXCEL记录面试结果,事件如:「面试已开始」、「简历已创建」、「面试已通过」是不会改变的,通过对不变业务的识别进行的建模,能保证模型的稳定且贴近现实,且有以下好处:
- 依据业务不变性所设计的产品方案,会更符合用户的真实需求
- 依据业务不变性所进行的技术方案,会更加稳定,系统大改的几率会更小
而这些最终都会体现在交付效率与商业价值上
Q:事实与观点的区别是啥?
A:事实是客观存在的,而观点由事实加工而来,并且可能会因人的主观意识而发生变化,举个例子:「人才最近教育经历」是由人才的所有教育经历加工而来,“最近教育经历”可以是包括了在读的教育经历,也可以不包括,取决于人的主观意识。
识别观点有助于我们在实现时保留事实,而不会因为产品方案发生变化,导致实现上的困难,想象一下我们只保留最近教育经历(观点),而不保存全部的教育经历(事实)。