DDD你真的理解清楚了吗(8)非敏捷实践_用例

DDD在非敏捷团队中的实践

自从2001年《敏捷宣言》横空出世,就一直有一种讨论,是不是所有的研发团队都应当敏捷转型。实际上,在软件研发这个产业中,包含各种各样的软件应用,它们既包括大型的业务应用系统,也包括嵌入式、桌面端等系统,不同的系统,软件规模、复杂程度、变更频率都不同。在有些行业的有些系统中,频繁变更、快速交付等需求没有那么强烈,反倒是按部就班、稳定开发、安全可靠更加重要。这时,敏捷开发就没有了优势,而是传统模式将成为首选。那么,传统模式的开发团队可以实践DDD吗?当然可以,关键是选择合适的实践方法。

前面谈到,从需求分析到领域建模,有很多种实践方法,它们包括原文分析法、四色建模法、事件风暴法。上期我们也探讨了,通过事件风暴法在敏捷团队中实践DDD。但是,如果你开发的系统是嵌入式或桌面端,业务比较简单,没有那么多业务流程和领域事件。这时,你可以选择最简单也最原始的建模方法:在需求讨论过程中领域建模。

譬如说,我们现在要开发一套智能温控系统。这时,我们来到了客户现场,跟客户开一场需求研讨会。在会上,客户开始描述他们的业务需求,而我们则运用“统一语言建模”的方法,一边聆听客户,一边仔细理解业务(关于“统一语言建模”可以看我之前的文章):

客户:我们要做的这个温控器被部署在每个房间中,用于控制每个房间的温度。每个房间都有一个HMI,用户可以在HMI上设置房间温度。同时,HMI也会显示每个房间的温度、湿度与空气质量。

我们:对不起,什么是HMI?(我们开始运用“统一语言建模”去捕获那些我们听不懂的专业术语)

客户:HMI就是人机交互接口,说白了就是我们经常使用的控制面板。但我们的温控器除了有贴在墙上的控制面板,还要做成移动APP安装在手机里。这样,用户使用起来会更加方便。

这样,我们在纸上开始领域建模,绘制出了HMI,可以显示房间温度、湿度和空气质量,还可以设置温度,这些都是HMI的方法。

我们:有了HMI,用户在设置温度以后,是不是就应该开始进行温度控制了?

客户:要进行温度控制,我们首先要有一个温度传感器去感应房间温度。当然,除了温度传感器,还要有湿度、空气质量等传感器。

我们:以后,是不是还可能增加更多不同类型的传感器?(我们一边顺着客户的思路在思考,一边开始运用我们的专业知识,提出更多有用的问题)

客户:是的。不仅如此,即使温度传感器可能也有各种类型的传感器,并且今后也可能不断增加新的类型。

我们:好的。这样,我们在设计的时候做成通用接口,支持日后的产品扩展。

客户:是的,没错!有了这些传感器,首先要将数据显示在HMI上。如果温度超过设置的温度就应当通过制冷设备制冷,如果低于设置的温度就应当通过加热设备进行加热。(客户显得越来越兴奋,这场探讨也渐入佳境)

我们:稍微等等。我认为应该在HMI与传感器、加热设备、制冷设备中间增加一个控制器!(运用我们的专业知识,开始提出一些我们的意见)

通过以上的讨论,我们在纸上绘制出了HMI、传感器、加热设备、制冷设备与控制器。这仅仅是最粗略的一个模型,但不仅我们,客户也能理解这个图形。

DDD你真的理解清楚了吗(8)非敏捷实践_用例_02

慢慢地,这个模型就成为了我们与客户更加细致地探讨业务需求的一个平台。在这个模型的基础上,客户提出了更多地细节,模型就由粗到细,绘制得越来越细致。HMI有LocalHMI和MobileApp两种,而传感器有温度、湿度、空气质量传感器,每种传感器又有很多种不同的实现方式……就这样,领域模型开始不断细化:

DDD你真的理解清楚了吗(8)非敏捷实践_用例_03

不同的HMI都做成标准的HMI接口与不同实现,不同的传感器也做成标准的传感器接口与不同实现,加热设备、制冷设备也都是这样。在这样的基础上,逐渐开始划分限界上下文,整个系统就可以划分成多个模块,由多个团队彼此独立地去设计开发。

有了这样的领域模型,是否就可以把需求分析清楚吗?好像不能。领域模型只是分析清楚了静态的数据结构及其相应的关系,却没有办法表达业务操作的流程。譬如,在本案例中,系统都要为用户提供哪些功能呢?每个功能都是怎样的流程呢?这就需要通过用例模型与用例描述来予以分析。因此,我们绘制了这样的用例模型:

DDD你真的理解清楚了吗(8)非敏捷实践_领域驱动设计_04

接着,为每一个用例编写用例描述,描述每个功能的业务流程。譬如,我们为“温度控制”这个用例编写用例描述:

DDD你真的理解清楚了吗(8)非敏捷实践_DDD_05

在用例描述中,不仅描述了业务流程,还将该需求中方方面面的内容都分析清楚了,如参与者、触发事件、前置条件、后置条件。而业务流程也通过事件流分为基流(即主流程,所有环节都成功的情况下的流程)、分支流(分支出去以后最终会回到主流程,即可选流程)、替代流(分支出去以后不会回到主流程,即出现某些异常,通过异常分支就直接结束了)。用例描述更加标准、更加全面,可以成为今后需求文档的编写方式。

将用例模型作为动态模型,描述业务流程;将领域模型作为静态模型,描述数据结构,就能将业务需求方方面面的内容都分析清楚。这样,就一个一个将业务需求设计到这2个模型中,并逐步细化。譬如,在以上设计的基础上,现在要添加“设置时间”这个功能,功能描述如下:

  1. 用户可以设置什么时间段启动温控器
  2. 温控器在启动时间内自动启动工作
  3. 温控器在其它时间段自动停止工作

原有的控制器是对温度的控制,即超过设定的温度就制冷,低于设定的温度就加热,这显然与当前的需求是不同的。按照“单一职责原则”,原来的控制器是进行温度控制,那么这个新的时间控制就应当设计成另一个控制器,与原来的控制器分离。因此,我们进行了如下的设计:

DDD你真的理解清楚了吗(8)非敏捷实践_业务流程_06

系统中有各种不同类型的传感器,并且在未来,传感器类型也会增加。每种类型的传感器也会有不同的实现方式,如温度传感器可以通过空气感应、地板感应……加热设备可以电子加热、锅炉加热、散热器加热、地板加热等多种方式,每种方式还有不同的规格。基于这样的需求,在领域模型中提取出了一个通用接口,然后设计各种不同的实现:

DDD你真的理解清楚了吗(8)非敏捷实践_DDD_07

当然,这样的设计依然还是太粗,后面一个一个地要去落实每个对象中的属性及方法。有了这样的领域模型,该如何落实到系统设计上呢?实际上,HMI、控制器、传感器、加热设备、制冷设备都是分布在房间中的多个独立设备,它们通过电线串联起来。它们之间的数据通讯就是基于领域模型的数据结构进行设计。与此同时,在领域模型中的通用接口,在设计实现时,研发人员就可以有意识地设计成一个可插拔的设备。这样,今后可以设计出各种各样的HMI、传感器、加热设备、制冷设备。这些设备可以任意组合,来满足用户各种各样的个性化需求,从而让产品有更强的市场竞争力。

DDD的真正价值在于日后的变更维护。传统的软件系统,随着日后的需求变更,程序会变得越来越乱,那么每次变更时就会变得越来越困难,代码质量越来越差。采用了DDD,在每次需求变更时,不是直接去修改代码,而是先进行用例模型和领域模型的变更,把该变更的设计方案梳理清楚。譬如,现在要在温控系统中增加“智能温控”,业务需求如下:

1)当房间内没有人时,温控器停止工作以降低能耗

2)当检查到房间有人时,自动启动温度控制

这个需求该怎样设计呢?首先,将该需求回到用例描述中,查看现有的业务流程需要进行怎样的调整,在分支流中增加了“智能温控”子用例,并在该子用例中详细描述“智能温控”的业务流程。

DDD你真的理解清楚了吗(8)非敏捷实践_DDD_08

接着,将该需求回到领域模型中进行分析。通过对领域模型的分析发现,要实现该需求,只需要增加一个新的传感器类型:感知是否有人的传感器,然后增加一个“SmartController”的控制器,业务就实现了。这样,就可以在日后变更过程中,用最小的代价,做出最优的设计,从而保证系统始终能低成本、长期持续地维护。

DDD你真的理解清楚了吗(8)非敏捷实践_业务流程_09

(待续)