1、概述
之前的文章本专题花了大量文字篇幅,介绍如何基于业务抽象的设计方式完成应用系统各个功能模块的设计工作。而之所以进行这样的功能模块设计无非是希望这些功能模块在具体的项目实施过程中,能够按照当时的需求快速的、简易的、稳定的、最大可能节约开发成本的形成可用的应用系统。接着,如果有必要,这些系统能够在更高的构建层面,共同形成服务平台。那么从本篇文章开始,我们就来一起讨论一下,功能模块如何以“积木搭建”的方式形成符合特定需求的系统,以及这些系统在有必要的时候,如何形成服务平台的问题。
在各个模块都完成了低耦合设计后,诸如以下形式的模块分层结构就会形成:
有了这些模块,我们就可以在进行某个具体项目实施时,按照最终客户的要求将这些模块正式组合起来形成可运行的应用系统。或者这样形象的理解这项工作,我们将在一个“积木底座”上,利用已有的积木搭建一个最终用户需要的乐高造型。积木就是各个模块,积木底座就是承载模块组合的运行容器(在Java生态中,典型的模块运行容器就是Spring-Boot,其中每个具体的模块实现都体现为一个Starter)。在搭建过程中设计人员会遇到很多问题,例如:不需要一些模块怎么办?一些模块需要但是功能匹配度不够又该怎么办?一些模块需要,但其下依赖的模块不需要该怎么办?等等。我们将在本文中对这些问题进行逐一讨论。
(乐高积木搭建的城堡)
2、将各个模块组合起来形成应用系统
这里讨论进行模块组合的前提是:各个模块是基于业务抽象的思想进行设计的。根据之前几篇文章介绍的内容,这种模块至少具有以下的设计特点:
-
经过业务抽象设计的模块,从设计层面就可以将抽象的模型、抽象的行为、业务无关的控制逻辑和具体的模型、具体的行为、具体的业务逻辑隔离开。
-
经过业务抽象设计的模块,其业务分层的位置是固定的。无论什么样的业务场景,该模块都不能被下层模块“看到”,既然无法“看到”也就谈不上被下层模块依赖。而且某个模块即使被上层模块所依赖,上层模块也只能依赖该模块的抽象模型和抽象行为。
-
经过业务抽象设计的模块,具有很小的涟漪效应,甚至没有涟漪效应。当模块发生变化时,这个变化产生的影响被限制在该模块内,不会向上层模块或者下层模块传递这种变化产生的影响;甚至这种变化对模块内部的影响也是有限的,当模块内部的某一种具体实现发生变化时,模块内部的其它具体实现也不会受到影响。
-
经过业务抽象设计的模块,对下层模块的依赖是有限的,且依赖链较短。这主要得益于模块固定的业务分层和只能依赖下层模块抽象模型、抽象行为的设计规则。
2.1、正常的模块组合场景
下图是一个经过业务抽象设计后,进行设计落地的典型工程结构,参与“搭积木”过程的各个模块,都以这种方式被提供给搭建积木的设计者:
从图中可以看出一个典型的模块结构中,设计人员将已经完成设计的抽象模型、抽象行为和控制逻辑独立出来形成一个工程结构。然后设计人员将模块的某种具体实现形成另一个工程结构,例如将基于本地数据库的默认实现形成一个工程结构,取名为X-Default-Local-Starter;将基于远程调用的具体实现形成一个工程结构,取名为X-Remote-Starter。
将各个模块组合在一起的工具,叫做模块组合器/模块组合层/应用程序启动器。整个搭建过程应该是由下及上的,也就是先确认和搭建那些和业务无关的工具性质的模块,以及那些虽然有业务性但是业务比较通用,适配度较高的功能模块;然后再搭建那些涉及业务但定制化风险不大的功能模块,最后才是那些存在较大变化风险甚至产品级别没有提供,需要项目团队完全进行定制开发的功能模块。
由于产品团队提供的A模块具有较强的业务性,在特定的项目中并没有这样的业务,所以项目的技术团队没有选择A模块。另外产品研发团队提供的G模块,项目的技术团队也没有选用,这也是基于项目的实际情况进行的决定。但有的读者也许会问,G模块被D模块所依赖,既然D模块都被选择