领域驱动设计工作坊
上QQ阅读APP看书,第一时间看更新

1.1.3 软件复杂度与变化

软件复杂度与变化之间的关系不言而喻。对于软件开发,变化是永恒的,唯一不变的就是变化本身。既然变化不可避免,那应该如何有效应对变化所产生的影响呢?接下来将围绕该问题展开介绍。

1.变化的表现形式和关注点

我们可以引用架构设计上的一个核心概念来回答上述问题,这个概念就是扩展性(Extensibility)。所谓扩展性,指的是当系统的业务需求发生变化时,我们对当前系统改动程度的一种控制能力。改动程度越大,扩展性就越低。扩展性低的本质原因在于代码组件之间的边界往往很难清晰划分。图1-14展示了代码组件之间的一个边界场景。

在图1-14中,我们看到了5个代码组件。代码组件是一种泛指,可以指向类、模块、服务、子系统等概念。在图1-14所示的边界之下,当向该系统中添加新业务时,假设只需要开发一个新的代码组件6替换原有的代码组件5,我们就认为系统具有较好的可扩展性。也就是说,不需要改变原有的各个代码组件,只需将新业务封闭在一个新的代码组件中就能完成整体业务的升级,这就是边界的力量。

图1-14 代码组件边界

另一个应对变化的核心手段是抽象。首先将系统中容易发生变化的点抽取出来并形成一个个扩展点,然后对扩展点进行替换就能完成系统的升级。图1-15展示了这样一种运行时的效果图。

C:\Users\TIANMI~1.ZHE\AppData\Local\Temp\ksohtml29256\wps3.jpg

图1-15 扩展点和SPI

在图1-15中,我们以SPI(Service Provider Interface,服务提供接口)的形式定义了扩展点。SPI是JDK提供的一种内置机制,是JDK中用来进行插件式管理的扩展点。JDK为SPI的执行提供了一种运行时环境。为了使用SPI,我们需要梳理系统的变化并将它们抽象为一个个SPI扩展点。如果我们能够抽象出合理的SPI扩展点,也就意味着可以合理地应对系统的变化。

2.变化的应对策略

应对软件系统的变化的基本思路是通过抽象顺应变化方向,并完成对系统的建模。统一建模语言(Unified Modeling Language,UML)为面向对象软件设计提供统一的、标准的、可视化的建模语言。它适用于描述以用例为驱动,以体系结构为中心的软件设计的全过程。图1-16展示的是商品管理业务场景下的UML用例图。

图1-16 商品管理业务场景下的UML用例图

图1-17展示了一张UML中的类图。通过类图,在定义类的时候,将类的职责分解为类的属性和方法。类在类型上可以分为实体类(Entity)、边界类(Boundary)和控制类(Control),实体类和边界类的划分与本章后续内容中介绍的DDD思想完全一致。映射需求中的每个实体而得到的类称为实体类,实体类保存要进行持久化的信息,而信息需要在用例内、外流动;边界类用于实现信息映射;控制类用于识别控制用例工作的类。

QQ截图20230628215926

图1-17 UML类图

UML一共提供了9种图形来表述业务场景中各个事物及其关联关系,如表1-1所示。

表1-1 UML图例

图片表格

至此,我们对软件复杂度进行了全面的剖析,图1-18对剖析内容进行了总结。

图1-18 软件复杂度各个维度下的关注点

可以看到,我们分别从规模、结构和变化这3个维度给出了软件复杂度对应的表现形式和关注点。这些表现形式和关注点为引入DDD思想和方法提供了理论基础。同时,架构师使用模型来表述系统,而模型是一个抽象概念,需要借助特定工具和方法进行表述。事实上,DDD也可以看作一种系统建模方法。