一家大型企业的关键业务代码已经年久失修成为了难以维护的遗留代码,有着硅谷高科技企业软件开发管理经验的高管决定在企业内部搞编写单元测试和重构的极限编程实践。这需要为企业遗留系统的代码编写自动化单元测试。那么编写自动化单元测试该从哪里入手呢?
上文所提到的“领域驱动设计中国峰会2017”大会Event Storming 之父 Alberto Brandolini授课一天的“事件风暴工作坊”下午的“软件开发设计”(Software Design)环节,可以让开发团队了解系统应该具备的领域模型及其交互关系,为编写单元测试进而驱动重构提供指导。
以下是实施步骤:
-
完成了“探索业务全景”环节。
-
确定要建模的需求
在上午探索培训机构的业务的基础上,下午的任务是要为“替换Eventbrite在线应用“这个需求进行领域建模。假设你是Eventbrite的开发团队且想了解如果入手编写单元测试,那么可以把Eventbrite视作遗留系统,用以下步骤来为其建模。 -
了解领域模型之间的业务逻辑链
从需求的起源来看,用户的需求来自于真实世界和 Read Model,并根据它们来进行 Decision Command(决策命令)。比如培训课程的听众根据“课程、价格、日期、讲师、城市、是否有空”这些 Read Model 来决定是否去报名参加培训。
用户做了 Decision Command后,就会通过 Aggregate (像个状态机)或 External System (外部系统)来产生一个 Event(领域事件)。
这些事件又可能会通过一个 Policy (业务规则)来触发下一个 Decision Command。
用户做了 Decision Command后,就会通过 Aggregate 或 External System 来产生一个 Event,而这又可能会通过一个 Policy 来触发下一个 Decision Command
我照着上图又画了一个版本
- 用7种报事贴来贴领域模型并梳理业务逻辑链
- Read model: 浅绿
- User: 黄色小报事贴
- Decision Command: 浅蓝
- Aggregate: 黄色大报事贴
- Event: 橙色
- External System: 浅粉
-
Policy: 紫色
按照从左到右的时间顺序来贴:
用7种报事贴来贴领域模型并梳理业务逻辑链
下面其中一组同学贴出的培训课程报名订票的业务逻辑链(报名者根据培训日期订票,通过一个未起名的 Aggregate 生成了 Ticket Booked 事件,这个事件又经由 Purchase Policy 触发了 Reserve Payment 的命令):
培训课程报名订票
事件风暴之父的忠告
-
领域建模达成一致是很困难的,要有充分的心理准备。
-
要邀请领域专家和团队所有成员,贴报事贴的空间要足够大。
贴报事贴的空间要足够大 -
在团队贴报事贴时,要观察不同业务部门的人的活动范围(比如财务人员不会跑到左边“培训课程准备子域”来贴报事贴,这很正常),这能启发你识别核心业务子域。有些核心业务子域是长长的一条,像泳道。有些则集中在一处,有些则会出现在一头一尾,这也很正常。
有些核心业务子域是长长的一条,像泳道。有些则集中在一处,有些则会出现在一头一尾,这也很正常 -
事件风暴领域模型聚集的方式有点像楼房——一片一片地出现。
一片一片地出现 -
开发团队不要盲目相信业务人员编写的用户故事,要合理地向业务人员 challenge 这些用户故事后面发生的事情。
不要盲目相信业务人员编写的用户故事 -
业务中的分支和开关会让你备受折磨,你需要衡量它们的全局价值成效来做取舍。
需要衡量业务分支的全局价值成效来做取舍 -
Aggregate 和 Policy 的名字很难起,可以以后再起名。
-
Policy 是一种响应式的业务逻辑,它一旦接收到 Event,就会触发一个 Decision Command。
-
有时候 Event 会通过"人工"的 Policy 来触发一个 Decision Command,但有时事实并不是这样的……
有时事实并不是这样的…… -
要识别那些“千夫所指”的业务瓶颈,首先消除这些最大的障碍,才能有效提升全局优化的成效。
首先消除这些最大的障碍,才能有效提升全局优化的成效
我的一些理解及 Brandolini 的回复
- Aggregate 可以理解为“数据完整性的维护者”。
- “千夫所指”的业务瓶颈可以作为编写自动化测试的第一个测试用例。
- 我试图把 Brandolini 的上述7种事件风暴领域模型与 Evans 的 Model-driven design 中谈到的7种领域模型按下面的方式对应起来,但Brandolini给了令我惊叹的回复:
- 我试图两者对应起来:
- Read model(浅绿)可以对应 Entities 和 Value Objects,而后两者可以用 Factory 来创建,其中 Entities 可以保存在 Repositories 里面。
- User(黄色小报事贴)没有对应。
- Decision Command(浅蓝,可以对其编写自动化测试)可以对应 Aggregates、Entities 或 Value Objects 里面的行为。
- Aggregate(黄色大报事贴,可以对其编写自动化测试)可以对应 Aggregates ,而 Aggregates 可以由 Factory 来创建,之后可以保存在 Repositories 里面。
- Event(橙色)可以对应 Domain Events。
- External System(浅粉,可以对其编写自动化契约测试)可以对应 Services。
- Policy(紫色,可以对其编写自动化测试)可以对应 Aggregates、Entities 或 Value Objects 里面的行为。
Evans 的 Model-driven design 中谈到的7种领域模型
Brandolini 的7种事件风暴领域模型
- Brandolini 的回复:
- Eric Evans的领域模型出现在CQRS之前。虽然这些模型目前仍然有效,但是CQRS的影响力更大。
- Read Model 如今是(DDD领域中的)一等公民。而将其对应到 Entities 和 Repositories 曾经是以前那些做法的薄弱环节。
- Policies 其实是被漏掉了。Eric 从来没有说过领域模型的搜集工作已经完成了。所以对于这一点没有所谓的官方建议。一些人创建了代表进程的聚合,另外一些人则将大量的逻辑推向了 Application Services(这通常让领域模型变得贫血)。 Greg Young 曾指出在这些方面应该有所变化。
- 我偏好于“对称性”的领域建模,即将 Aggregates 将 Commands 转化为 Events,而 Policies 对 Events 作出响应并调度 Commands 来进行工作。
- 我试图两者对应起来:
总结
对于要为企业遗留系统的代码编写自动化单元测试的开发团队,可以在进行了“探索业务全景”环节之后,使用“软件开发技术”环节中的识别7种事件风暴领域模型的技术,来优先梳理那些“千夫所指”的有业务瓶颈的业务的逻辑链,然后可以开始对其中的 Decision Command、Aggregate 和 Policy 编写自动化单元测试。用这些单元测试来驱动遗留系统代码的重构,让遗留系统的代码变得易于维护。
网友评论