重析业务逻辑架构模式,浅浅滴
记得大约在一年前,我曾写过相关议题的文章,叫做业务逻辑架构模式(事务脚本,表模块,活动记录,领域模型)和再谈业务逻辑架构模式(事务脚本,表模块,活动记录,领域模型),经过这一年多做项目的沉淀,特别是最近十天来,我又仔细研读了<Microsoft.Net企业级应用架构计设>这本书,并上了iteye论坛相阅了相关贴子,发现以前的很多认识是不全面的,甚至是错误的,相关的概念与认识也有了进一步的明确与清淅.觉得甚有必要将最新的一些想法与反思记录下来,达到总结提高的目的.
一.总论
首先来看一张图:
我的第一篇文章里曾出现过类似的图,但是现在若干细节上有所不同,下面一一来分析
二.事务脚本
这是一种最直接的业务架构模式,他将后台所有的模块(包括我们常说的Dao,Server,Entity,BL层等)整合在了一起形成一个层,使用的是最彻底的用例驱动----UI上有什么样的操作, 这一层就有什么方法.没有专门表达数据结构的类,所有对数据的操作都是直接的Sql方式.
三.表模块
虽然这种模式还是把所有的模块都整合成一个层,但是已经开始有了对数据基本的抽象了.在.net里,就是将数据库抽象成一个DataSet,将其中的每一张表抽象成一个DataTable.对数据的操作,就是对这些DataSet与DataTable的操作.一般来讲,使用这种模式的BL层的类内置一个DataSet或DataTable类型的成员或属性来装载数据,所以这种BL类一般表达的是对整张表或者是对数据集合的操作,但是因为DataSet或DataTable在表达数据方面是弱类型的,所以在表模块里是没有办法使用强类型的对象的.
四.活动记录
虽然这种模式还是把所有的模块都整合成一个层,但是与表模块相比,有两个值得注意的地方.
1.在这种模式里,对于数据的抽象就更进一步了----通过对象来表达数据,也就是对数据的强类型抽象.
2.一般来讲,使用这种模式的BL层的类,其属性就是数据库的列,一个对象就是数据库中的一行数据,所以其表达的操作是对数据行的操作.这也是与表模块模式最大的不同.
在.net里,ActiveRecord框架就专门表达这种模式的框架,通过其底层封装的类库与代码生成工具, 能够很快生成与这种模式相适应的.net代码.
五.领域模型
这种模型是我这篇文章的重点.在领域模型里,通过对BL层的分解来达到复用与灵活.按照iteye资深会员robbin的观点(我也基本赞同),特将此模型按实现方式的不同再分为以下4小类:
1.失血模型
在这种模型里,有一个Entity类来抽象数据,其只有属性,没有方法来表达业务,还有一个EntityManage类来表达领域逻辑与业务逻辑.
2.贫血模型
在这种模型里,有一个Entity类来抽象数据,用属性表达数据,还有少许不依赖其它层次与技术的领域逻辑,用属性或方法表达.还有一个EntityManage类来表达依赖其它层次与技术的领域逻辑和业务逻辑.
3.充血模型
在这种模型里,有一个Entity类来抽象数据,用属性表达数据,还有大部份的领域逻辑与业务逻辑,用属性或方法表达.还有一个EntityManage类,仅仅封装事务和少量逻辑,主要起一个外观的作用.
4.胀血模型
这种模型与活动记录比较像,就是不再分层,而是把所有的层全都放在一起.
在这四种模型当中,失血与胀血是应该不被提倡的.胀血模型与活动记录差不多,而把其中所有的逻辑拿到另外一个类中后就成了失血模型,两个都走了极端.剩下的两个模型:贫血模型与充血模型,就要视情况而定了.在我看来,我更推崇贫血的领域.如下图所示:
先来解释两个名词:领域逻辑与业务逻辑.所谓领域逻辑,就是领域本身特有的,不依赖具体执行环境的逻辑.比如一份合同,有签订时间与有效期,那么计算过期时间就是他的领域逻辑.业务逻辑,则是与领域相关,依赖特定执行环境的逻辑.比如合同订金,是依赖于合同的种类与金额大小的,有的15%,有的3%,有的签订合同当场就要缴纳,有的则有其它的条件与时限.
首先看领域层.领域层分为两部份:领域对象与领域逻辑.领域对象就是领域层的静态部份,通常表现为属性,在大部份的程序里他的大部份都会与数据库的列一一对应.领域逻辑是领域层的动态部份,是那些不依赖其它层次与技术的领域逻辑.在上图中可以看到我把领域层与表现层衔接了起来,表示领域层的对象也可作为DTO传到表现层上面去.
然后再看服务层.包括了依赖其它层次与技术的领域逻辑和业务逻辑.服务层是系统的核心,系统中的各个层次与各种技术都要与他发生交互.在上图中,上承表现层,下调领域层,通过IOC注入执行环境,通过AOP分离系统逻辑,通过ORM透明持久化,通过MSDTC定义事务边界等等.
在这里,有几点需要重点说明
A 在我看来,领域层是可以复用的,是面向对象的,为了达到这个目的,就一定要保证领域层的"纯洁" .所谓"纯洁",就是指:a) 领域层不能依赖任务具体的执行环境.比如我现在在做国土业务,政府在卖地的时候,会发几样凭证,其中一个叫<建设用地批准书>,一个叫<供地合同>.全国各地都是这么发的,但是发证的顺序与科室却有所不同,有的是M部门发A,N部门发B,有的是Q部门AB一起发.为了使这两个领域模型重用,就必需不能包含以上发证逻辑;b) 领域层不能依赖于具体技术实现.比如现在用的比较多的是NH,但是我建议还是不要让领域层依赖于他.万一重用后的环境用的是EF,就麻烦了.有时,为了简化程序层次,也可作为DTO传到表现层上面去.
B 在我看来,服务层是用例驱动的,是面向过程的,大部份情况下是无法实现复用的.为了帮助领域层保持"纯洁",服务层就不得不"不纯洁"了.就如同上面所说的,上承表现层,下调领域层,通过IOC注入执行环境,通过AOP分离系统逻辑,通过ORM透明持久化,通过MSDTC定义事务边界等等.系统中所有的一切,都在服务层形成了交汇.
C 在Asp.Net WebForm环境中,有时简单起见,会把页面的CodeBehined代码与Server层合并.这样可以减少系统的层次.不过如果系统比较大,比较复杂,还是不要这么干.
D 真正好的领域模型是重构出来的.一个行业的信息化是一个从不稳定到稳定的过程,一个业务的理解也是由浅入深的过程.在一开始,可能我们不确定这是不是个核心业务,业务的具体表现形式也可能多变,这时可能我们会把逻辑写到CodeBehined里;经过一段时间的沉淀后,可能觉得这个业务较为通用,表现也趋于稳定,这时我们就可能会把其重构到Server层里;最后我们发现其实这个业务经过更高级别的抽象后有其不变的地方,这时我们就会考虑将其技术细节剥离后放入领域模型.
E 在我看来,复杂查询,报表与统计在本质上不是面向对象的,而是面向数据的,且业务非常不稳定,那么他们也就不必通过领域模型来完成.而是直接从Server层连接到DB来完成相关功能.
F 领域驱动设计与领域模型虽然经常配合起来使用,但其实是两个层面上的东西.领域驱动设计,强调的是在业务分析过程中,以业务为核心进行分析建模,是一种分析方法论;而领域模型则是一种强调在技术实现中,以业务为核心,是一种技术实践的方法论.
在我看来,充血模型会提高领域层的耦合性,在业务上耦合了特定业务场景,在技术上耦合了特定技术选型,所以我更倾向于使用贫血模型.
其实以上文章只是我在结合了自己的理解后炒的冷饭,早在5,6年前,iteye论坛就已经组织讨论过相关话题,02年POEAA就出版了,想到自己的相关技术水平还只停留在别人10年前的水平,感觉很是惭愧.
以上的贫血模型,其实只能比较好的解决中小规模的项目.一旦项目复杂度上到一定的程度后,此模型仍然运作的不是很理想.下一步,就是要进一步的研究近些年出现的相关知识,如DCI,四色模型,CQRS等.争取从更高层次完成对系统的拆分,实现系统更理想的复用!在这方面,园子里已经有两位先行者了:dax.net与netfocus.有时候,能站在先驱与巨人的肩膀上学习,真是件很幸福的事啊~~~
引用的文章
一.关于领域模型
二.贫血VS充血
为什么java里不能把域对象和DAO合并,rails里面就可以?
对Robbin《domain model的延伸讨论(重新编辑) 》一文质疑
Domain Object贫血vs富血(DDD)和spring roo到ruby的扯淡
三.其它