在业务层的模式上有几种选择。首先是过程式模式,在面向对象开发兴起之前,业务逻辑只不过是一系列过程的集合,每个集合都用来处理来自于表现层的一个请求。因此好的设计都在于更好的去组织这些过程,减少代码和流程的冗余。基于对象的模式抽象程度越高,与数据模型的差距也会越大,因此若想创建一个领域驱动的对象模型,一般应该从领域着眼,而不是从数据库结构开始。领域驱动的设计一定会使数据库模型和领域模型之间存在不小差异。这个模式通常叫做领域模型。
基于对象的模式在开始时代价较高,但随着复杂性增长,其代价基本上也线性增加。换句话说,一旦你对领域模型有足够的了解,无论系统的复杂性如何,你都可以使用领域模型模式。在复杂性度量上,复杂性分为两方面,第一种是天生的,不可避免,无法协调需求本身,这类复杂性可以通过一些方法或多或少降低,不过不可能完全抹除,必须直接面对。第二种是来自于引入的复杂,这个来自于不同项目干系人意见的不协调,负面效果就是将团队引向错误的方向,错误的方向则会让抽象设计包含不必要或非功能性的功能,将软件逼上绝路。
需求的数量可以用来粗略判断系统的复杂性,认识到复杂性比解释如何找到复杂性更加简单。
1 事务脚本模式
事务脚本模式的关注点在于用户通过表现层所能执行的操作,并为每个操作编写一个专门的方法,这个方法就叫做一个事务脚本。
“事务”在这里指一个需要执行的业务流程。“脚本”表示在业务流程上执行的一系列操作。
1.1 何时使用?
事务脚本易于理解和解释。每个必须的用户操作都在一个事务中开始,直至结束。不过数据访问通常被封装在另一个组件中,并不属于脚本的一部分。
事务脚本给出的模型中的逻辑均是使用IF、WHILE和FOR等语言元素表述的。向现有用户操作中添加新功能就意味着添加一个新分支或在现有代码的某处添加一个新的子程序。
事务脚本适合应用于那些业务逻辑非常简明直白,且最好不大可能改变的简单场景中。
1.2 优势
事务脚本是一个简单的过程式模型,它以逻辑事务的集合的形式来表现应用程序的逻辑。使用过程式方法并不意味着需要编写大段、不可拆分的代码。一个高层次的事务可以被拆分成更简单、可重用的子程序或层。最常见的一个层就使持久层,其中你会发现一些小的、用来处理数据访问以及相关事务的组件。下图所示为基于事务脚本的系统的大体结构。
1.3 劣势
简单是事务脚本模式最值得一提的优势,但是事务脚本有造成代码重复的潜质。所有实现了事务脚本的类型都可以看做是业务组件,每个业务组件都可以有一个或多个事务。比较流行的设计方式是将各个事务脚本分组,然后让每一组成为一个业务组件,这样每一组中的事务脚本在逻辑上都是相关的。另一种做法是将每个事务脚本用单独的类封装起来,这样每个业务组件都仅包含一个方法,换句话说,这里的业务组件就变成了一堆命令对象。若想将事务脚本的业务组件设计成命令对象,可以释放出一个接口,并将所有需要的参数以类型公有属性的形式暴露出来。
2 表模式
与事务脚本相比,表模块更有结构,表模块作为一个容器,将数据和行为组合在一起。业务逻辑被拆分为粗粒度的、表示整个数据表的组件。表模块的粒度不会下降到数据行的程度,即表模块类无法区分每一行数据。表模块模式应用中,使用基于记录集的数据结构是最常见的方式。例如Dataset、Datatable等。
对那些不需要太多抽象,且数据模型和对象模型之间没有太大差异的场景,表模块是个非常不错的折衷方案。与事务脚本相比,表模块基于一个概念模型。而不是一大批方法的松散集合。若系统中的表现层和数据访问层都是基于表状数据结构,那么表模块将是非常好的选择,这时,业务层即可为表现层提供直接可用的数据,有时甚至可以直接通过数据绑定实现。
表模块并没有比事务脚本复杂很多,不过若完全需要手工构造,那么花费的时间可能比简单的事务脚本多出很多。模式在提供了更多指导的同时,也意味着我们需要考虑更多规则,也就是编写更多代码。
内嵌的Dataset设计器提供Fill和GetData方法来基于整个数据表进行查询。从概念角度讲,表模块的另一个优势在于,无论底层数据源是什么,对于同一些功能都会得到同样的表模块类。表模块基于对象,但完全由数据库驱动。表模块并不适合表述复杂的众多实体。特别是对象模型和数据模型之间有显著差距的时候。表模块的优势是VS提供了强大的支持,实现起来简单,不过考虑到这些生成的代码类似于一个黑箱,优势也就成了劣势。当然,表模块这个结构模型不一定需要和VS紧密耦合。
3 活动记录模式
事务脚本和表模块都是过程式模式。但是表模块基于对象,不是一个对于基于对象业务逻辑建模模式。最终走向面向对象设计关键的一步是你认识到系统中的目标对象,业务逻辑和领域逻辑才是系统的核心,它是由实体之间必须的交互组成的。
活动记录是一种应用于相对简单的领域模型,但仍基于对象模式。另一种更加容易理解的说法是,活动记录基于数据表中的行,而不是表模块那样基于数据表。也就是说,活动记录对数据有行级别的粒度,而表模块关注的是整张表。活动记录模式归为数据源设计模式,广州达内而不是业务逻辑模式。活动记录模式就是指一个封装了数据表或视图的一行的对象,对象中可以同时包含数据和行为,活动记录对象的结构应该尽可能地接近于相关联的数据表结构。
在那些没有服务层的应用程序中,活动记录模式可以和事务脚本结合使用。
活动记录模式的成功依赖于两个因素:简单和框架。这两个因素紧密相关。我们最常见和熟悉的是LINQ- to - SQL。相对于简单的逻辑来说,活动记录非常合适。活动记录的另一个问题是对象和数据库表设计之间的绑定。若你不得不修改数据库,那么同时也要修改活动记录对象模型以及所有相关代码,从这个角度看,活动记录站在了领域驱动的对立面。
4 领域模型
在设计领域逻辑时,若选用了过程式方法或活动记录模式,那么实际上采取的是以数据为核心的做法。驱动业务模型设计的并不是业务本身,而是业务中使用到的数据。
从长远看,以数据为中心的方法并不能很好的适应规模的增加。因为当系统的复杂性达到某个极限之后,哪怕是再添加一些新的需求,都会成为难处理的问题。领域模型关注于系统的期待行为以及系统正常工作所需要的数据。领域驱动设计力求将复杂性控制在一个可控(虽然仍旧很高)范围内,选用领域驱动设计并不一定需要使用领域模型模式,不过领域模型模式是最自然的选择。
领域模型描述了系统中涉及的实体,捕获了实体之间的关系以及数据在其中交换的过程。领域模型此刻还并不是一个软件,也不涉及任何软件或实现上的概念或原则。领域模型只是一个正式的模型,用来让技术团队和业务团队彼此理解并能良好交流。领域模型可以看作是活动记录的“大哥”,领域模型完全和数据库独立,是一个尽可能贴近真实流程的模型。领域模型模式使系统建模非常自由灵活,无需考虑平台和数据库的约束。此外,领域驱动设计是一种看待设计的方式,因此技能和态度起到了重要的作用。
在领域模型中,概念是用来建模的,这种做法充分利用了面向对象设计的优势,你可以使用到面向对象的所有特性,包括封装和继承,而同时又无需受到数据库结构的限制,这就意味着实体并不会察觉到丝毫有关底层使用的持久化机制。领域模型是由需求驱动的,且仅需要你理解问题领域,并用类对于逻辑建模。实现领域模型主要的障碍是对象和关系型数据库之间的不匹配,领域模型是一个面向对象的设计,和数据库或其他应用程序的约束完全无关,而今受限于其建模的业务流程,这意味着同样的领域模型可以重用于其他需要同样逻辑的任意场景。模型是和业务相关的,且仅和业务相关。
参考《Microsoft .NET 企业级应用架构设计》第四章业务层