- 企业应用架构模式(典藏版)
- (英)马丁·福勒
- 2299字
- 2024-06-18 18:42:22
Chapter 2
第2章 组织领域逻辑
领域逻辑的组织可以分为三种主要的模式:事务脚本、领域模型以及表模块。
保存领域逻辑最简单的方法是使用事务脚本。简单地说,事务脚本是这样一个过程:从表示层获得输入、进行校验和计算处理、将数据存储到数据库中以及调用其他系统的操作等。然后,该过程将更多的数据返回给表示层,中间可能要进行更多的计算来组织和格式化返回值。基本的组织方式是让每个过程对应用户可能做的一个动作。所以,我们可以将这一模式想象成一个动作或业务事务的脚本。该脚本不必一定是单个的内联过程,还可以分离成不同的子例程,这些子例程可以在不同的事务脚本之间共享。但是,每一个动作还是由一个过程来驱动。例如,一个零售系统可能会有结账、将商品放到购物车、显示交货状态以及其他一些事务脚本。
事务脚本具有如下几个优点:
❑它是一个大多数开发者都能理解的简单过程模型。
❑它能够与一个使用行数据入口或表数据入口的简单数据源层很好地协作。
❑设定事务边界的方法显而易见:一个事务始于其脚本的打开,终于其脚本的关闭。很容易用工具在幕后设定事务边界。
可悲的是,事务脚本也存在许多缺点,当领域逻辑的复杂性增加时这些缺点就会凸现。当若干个事务需要做相似的动作时,通常会出现重复的代码。通过将这些代码提取出来组织成公共的子例程可以部分消除这种情况,但即使如此,很多重复仍然难以消除和发现。这使得应用程序没有清晰的结构,变成了一张由许多例程组成的极度杂乱无章的网。
当然,复杂逻辑情况的处理必然要引入对象,解决前述问题的面向对象方法就是使用领域模型。我们建立一个关于领域的模型,至少在开始的时候主要围绕领域中的名词来组织。例如一个租赁系统会有租约、资产等类。进行校验和计算的逻辑会置于领域模型中,因此发货对象可能会包含计算一次运输费用的逻辑。可能仍然有计算账单的例程,但它实际上会迅速委托给领域模型方法。
用领域模型而不是事务脚本正是面向对象的程序员所经常谈论的“范式转换”的精髓。在领域模型中,不再是由一个例程来控制用户某一动作的所有逻辑,而是由每一个对象都承担一部分相关逻辑。如果不习惯领域模型,开始学习使用它时会充满挫折感,为了找到行为在哪里,你会从一个对象冲到另一个对象。
用一个简单的例子来说明这两种模式的区别比较困难,但是在关于模式的讨论中,我还是选择了一个简单的领域逻辑片断,试图通过用这两种方法建模来说明它们的不同之处。最易体现出区别的是这两种方法的顺序图(见图2-1和图2-2)。核心的问题是:对于同一给定的合同,不同种类的产品有不同的收入确认算法(关于应用背景,请参见第9章)。计算收入确认的方法中必须先确定给定合同中产品的种类,然后应用正确的算法,之后再创建相应的收入确认对象来保存计算结果。(为简单起见,我省略了与数据库的交互问题。)
图2-1 使用事务脚本计算收入确认
图2-2 使用领域模型计算收入确认
在图2-1中,事务脚本中的calculateRecognitions方法完成了所有的工作。底层对象只有一些表数据入口,它们仅仅完成将数据传送给事务脚本的任务。
反之,图2-2中有多个对象,它们每个都向前传递一部分行为给另一个对象,直至策略对象创建了结果。
领域模型的价值在于你一旦掌握了它,就可运用许多现成的技术来较好地组织日趋复杂的领域逻辑。例如,当增加新的收入确认算法时,只需增加相应的新策略对象即可。而使用事务脚本则需要在脚本的判断逻辑中增加许多新的条件。如果你也成为像我一样坚定的面向对象信徒,你甚至会在相当简单的案例中也宁愿采用领域模型。
领域模型的开销来自使用上的复杂性和数据源层的复杂性。刚接触复杂对象模型的人需要时间来适应复杂的领域模型。通常,开发者要在采用这一模式的项目上工作数月后才能转变他们的思维方式。但是,一旦习惯了领域模型,一般就可以在将来很好地运用它,从而受益终生。当然,的确存在不少开发者,他们似乎总也无法适应这种转变。
即使成功地完成了这一转变,还需要面对将领域模型映射到数据库的问题。运用领域模型越充分,当你将它映射到关系数据库时就越复杂(通常使用数据映射器)。复杂的数据源层就好似一笔固定资产投资——你需要付出相当多的钱(如果你想买)或时间(如果你想自己建造)才能得到一个良好的数据源层,但一旦拥有了它,就可以利用它完成许多工作。
第三种组织领域逻辑的模式是表模块。这一模式乍看起来与领域模型很相似,它们都有合同、产品和收入确认类。关键的区别在于领域模型对数据库中每一个合同都有一个相应合同类的实例,而表模块只有一个公共的合同类实例。表模块设计成与记录集一起工作,因此,在一个用来处理合同的表模块中,客户需要首先对数据库进行查询以生成一个记录集,然后以记录集为参数创建一个合同对象。客户可以调用合同对象的方法来完成各种操作(见图2-3)。如果客户要对某个指定的合同进行操作,它就必须在调用方法时附加该合同的ID。
图2-3 使用表模块计算收入确认
在许多方面,表模块是事务脚本和领域模型的一个中间地带。它围绕表而非直接围绕过程来组织领域逻辑,提供了更多的结构,而且更容易发现和移除冗余代码。但是,你无法应用许多在领域模型中可以使用的组织细粒度逻辑结构的技术,例如继承、策略和其他面向对象的设计模式。
表模块最大的优点在于其与软件架构中已有部分的衔接。许多GUI环境在设计时都假定其将与SQL查询的返回结果协同工作,这些结果是以记录集的方式组织的。表模块也工作在记录集之上,因此你可以很容易对数据库进行一次查询,然后在表模块内对返回结果进行操作,再把操作完成后的数据传给GUI显示。你也可以在将用户界面中修改的数据回传到数据库时,使用表模块来完成计算和校验。许多平台都使用这种开发网格,尤其是微软的COM和.NET。