对领域模型实现的总结性观点

陶文发起的对领域模型的最新讨论:领域模型的价值与困境,在这个讨论当中,我的关注点是,在现在的技术水平下,我们如何把领域模型的理论和我们实际应用开发框架结合起来,总结出最佳实践:

第一、DAO层和TransactionScript层是邪恶的!

我们在2004年一直跨度到2007年讨论来讨论去,其实都有一个隐含的前提条件:你的领域模型终究无法脱离对DAO层的依赖,以及需要TransactionScript层的包裹。而这样一来,领域模型的通用性、可移植性、业务逻辑的表达能力基本全部丧失,沦为框架限制下的奴隶。

而我们看看现在Java领域的技术进步,JPA已经普及,EJB3的隐含事务管理,甚至连Spring也可以简化成@Transactional,现在已经是我们可以去掉DAO和TransactionScript的时代了。


第二、Seam在消除了DAO层和TransactionScript以后,领域模型的感觉已经呼之欲出

这个不用多说,大家自己去看Seam文档好了。我唯一想强调的是entity bean和business bean分开有没有必要性,我的看法是有必要!这还是和Java本身语言的限制有关系:

1、Java语法表达能力有限,entity bean又不得不弄一大堆getter/setter方法,都放在一个class里面,代码的可阅读性非常差

2、business bean的很多业务逻辑方法需要容器环境,不像Rails的model可以直接mixin进来

3、Java做为目前最主流的工业语言,开发团队都是大规模编码协作的,你都放在一个class里面,团队协作会遇到很大的麻烦(事实上RoR现在也有这样的问题,但是RoR开发效率高,往往不需要那么大规模的开发团队)

3、领域模型不同类别的业务逻辑可以很容易的分到几个不同的business bean里面,这样对团队协作的好处很大。


第三、不考虑框架限制,All-in-One的领域模型好不好?

比方说RoR的model就是All-in-One的,但是一旦出现可以抽象出来,比较通用的业务逻辑,我们还是会把这些逻辑抽出来,单独实现,然后再mixin回去。所以没有必要All-in-One,根据你的实际需要来决定放在一个class里面,还是拆开成几个class。

所以最终我对这个问题的总结就是:

一、只要技术框架能够实现,尽量使用领域模型

二、无论Java还是Ruby,必须消灭DAO和TransactionScript

三、领域模型不必All-in-One,Java可以分割为 1个entity bean和几个business bean,而Ruby可以分割为1个model和几个mixin的module。


15:16 浏览 (3482) 评论 (22) 分类: Java 收藏 相关推荐 评论
terranhao 8 小时前 回复
为什么使用seam能消除DAO?
对一些查询,我还是需要一个DAO来封装啊,要不每次需要select e.* from entity as e where...,我都得调用entitymanager来执行这个sql?
如果用dao,我直接注入一个无状态的dao,然后调用dao就可以了,也避免了代码重复啊
期待解答。
netbaixc@gmail.com 昨天 回复
upheart 写道

netbaixc@gmail.com 写道
呵呵,职责单一了不就是应对变化啦?具体说,就是一个类就干这个活,不要干A还干B,否则如果仅仅干A的方式改了你就得去改这个类,重新部署测试;DAO就负责持久化的活,像你说的,如果数据库不同了持久化方式要改变则就改DAO;DOMAIN就负责业务逻辑,比如某个财务数据的计算方式变了,就改DOMAIN;Service负责完成用户和系统的基本交互;Command则是针对所有类型的用户交互的服务,因为有些时候有那种“快捷按钮”,你一点就要求系统给你做“一串”处理,所以Command就可以组织发起几个Service。这样按这种分类标准划分的类,哪个方面要变化,就改哪个方面啦。职责似乎可以无限细分下去,这取决于你对变化的预期,如果我预期存储方式99.9%不会变,我是不是就可以先不考虑用一层DAO呢?我做一个web应用,现在也用不着command,没有撤销前进的功能。当然我还是主张职责单一的,我只是不主张无谓的分层。


DAO和DOMAIN看上去不是界限分明的,很明显地为了提高效率往往把可以java运算的业务逻辑用复杂sql表达,相应的domain就架空了,这时domain的方法就直接调用一下dao对象的方法即可,当然有时候可能要发个邮件短信什么的,那指定是不能放到dao里的。DOMAIN和SERVICE也不是界限分明的。但是其实分层的意义在于它是和整个软件工程协作的。我们做软件时总是先定义需求用例,这时候能够划分出command,再从中抽象提炼出service来供command组合调用。一般情况下用户和系统的交互用例都需要事务保护,自然而然地service层里就会体现事务保护,有时还有业务锁同步逻辑。当我们再往下到概要设计时,就会从service里抽象出很业务的domain出来,每个domain都面向比较稳定的业务职责,相互之间也是可以组合调用的,统一交给service来组合调用,这里多数情况又会发现一个service基本上就是调用一个domain,“界限也不是那么分明,何必分层呢”,最后到详细设计实现时那些数据库的活就从domain里明确出来,自然而然地就出来了dao层,当然这时多数情况下又发现一个domain就是调用一个dao拉到,又何必分层呢?呵呵,比较绕。其实分层是这个过程自然的结果,既然这个过程自然产生了分层,何必要抹杀这个成果呢?毕竟分层达到了良好的OO效果,利用代码可读性可维护性,在IOC环境下更加明显。但我其实开始想说的是必须要有辅助工具来帮助程序员面对一个现实,就是这样的分层产生了太多类文件,对于开发人员手工开发起来一不小心打错个字符啥的,还要命名那么多方法,太辛苦啦,开发人员又觉得没有创造性太八股,很难管理啦!虽然设计和需求人员觉得总算可以通过非编程语言和开发人员交流(限制其创造性)了!
upheart 前天 回复
netbaixc@gmail.com 写道


呵呵,职责单一了不就是应对变化啦?具体说,就是一个类就干这个活,不要干A还干B,否则如果仅仅干A的方式改了你就得去改这个类,重新部署测试;DAO就负责持久化的活,像你说的,如果数据库不同了持久化方式要改变则就改DAO;DOMAIN就负责业务逻辑,比如某个财务数据的计算方式变了,就改DOMAIN;Service负责完成用户和系统的基本交互;Command则是针对所有类型的用户交互的服务,因为有些时候有那种“快捷按钮”,你一点就要求系统给你做“一串”处理,所以Command就可以组织发起几个Service。这样按这种分类标准划分的类,哪个方面要变化,就改哪个方面啦。


职责似乎可以无限细分下去,这取决于你对变化的预期,如果我预期存储方式99.9%不会变,我是不是就可以先不考虑用一层DAO呢?我做一个web应用,现在也用不着command,没有撤销前进的功能。

当然我还是主张职责单一的,我只是不主张无谓的分层。
jerryeye 前天 回复
DAO做的事情不做了吗?不做了当然取消,做了却取消,只是搬了个地方而已,这样有意思?DAO只是三个字母而已,惹谁了?大概robbin被国外的"血型"弄的迷糊了。
netbaixc@gmail.com 前天 回复
upheart 写道

为什么要分层呢?——大部分人是为了分层而分层,比如我们以前的一个做法就是强制一个实体对应一个dao、一个service一个action等等,就像是写八股文章,还美其名曰规范化,为了以后好维护。结果都是一两行的代码在几层之间包来包去。我觉得分层最主要的目的是解耦。很多人可能觉得分层是为了分类职责,这可能是分层带来的一个好处,但不是主要目的。要的目的是解耦,解耦的主要目的是应对变化。比如我分一个dao层,可以屏蔽掉hibernate或jpa或jdbc的具体实现,这样我可以切换不同的实现了。你看spring里边的petstore例子提供了不同的实现。问题是对于变化我们应该什么态度。我觉得应该在三个方面考虑:1.变化发生的可能性和机率;2.提前准备应对这个变化要增加的成本(包括机会成本)3.如果不提前考虑这个变化,以后通过重构来应对的代价。比如你有多大的可能要切换hibernate到jdbc或其他的实现呢?如果hibernate已经被证实足够稳定,那依赖它有什么问题呢?就像你依赖java.util一样是吧。这种被Rod JSon成为“幻影需求”提前为此而分层实在不值得吧。其实就算你分了层以后切换的时候还是会有很多问题的,除非你的应用就像petstore一样简单。所以与其这么做,不如完善你的测试,如果变化来了,有测试做保障,提炼重构付出的代价不一定比你提前预防这种变化的的代价要大很多。而且一定要考虑这种变化的机率的大小。分层的另一个好处是可以做孤立测试,说白了就是分离职责然后单独测试。但我前面说过分离职责不一定要分层的,你可以把业务复杂职责单一的功能放在另外一个类中。我现在的做法是用spring web flow做控制层,不需要java代码,只要xml的flow定义,其中调用service层。这个service层负责事务边界也代理业务,直接使用JPA接口。domain对象除了jpa等元数据的映射,提供一些get方法(我是说计算方法,比如getTotalSize()),还提供一些验证逻辑。页面使用Rich Faces。service测试大部分不做mock,而是连接数据库做测试,spring test框架支持自动回滚,可以用dbunit初始化测试数据就可以了。业务计算复杂的提炼出一个类来可以做mock测试了。可能以后还想加上selenium做验收测试,现在还没有加。


呵呵,职责单一了不就是应对变化啦?具体说,就是一个类就干这个活,不要干A还干B,否则如果仅仅干A的方式改了你就得去改这个类,重新部署测试;DAO就负责持久化的活,像你说的,如果数据库不同了持久化方式要改变则就改DAO;DOMAIN就负责业务逻辑,比如某个财务数据的计算方式变了,就改DOMAIN;Service负责完成用户和系统的基本交互;Command则是针对所有类型的用户交互的服务,因为有些时候有那种“快捷按钮”,你一点就要求系统给你做“一串”处理,所以Command就可以组织发起几个Service。这样按这种分类标准划分的类,哪个方面要变化,就改哪个方面啦。

upheart 前天 回复
为什么要分层呢?——大部分人是为了分层而分层,比如我们以前的一个做法就是强制一个实体对应一个dao、一个service一个action等等,就像是写八股文章,还美其名曰规范化,为了以后好维护。结果都是一两行的代码在几层之间包来包去。

我觉得分层最主要的目的是解耦。很多人可能觉得分层是为了分类职责,这可能是分层带来的一个好处,但不是主要目的。要的目的是解耦,解耦的主要目的是应对变化。比如我分一个dao层,可以屏蔽掉hibernate或jpa或jdbc的具体实现,这样我可以切换不同的实现了。你看spring里边的petstore例子提供了不同的实现。

问题是对于变化我们应该什么态度。我觉得应该在三个方面考虑:1.变化发生的可能性和机率;2.提前准备应对这个变化要增加的成本(包括机会成本)3.如果不提前考虑这个变化,以后通过重构来应对的代价。

比如你有多大的可能要切换hibernate到jdbc或其他的实现呢?如果hibernate已经被证实足够稳定,那依赖它有什么问题呢?就像你依赖java.util一样是吧。这种被Rod JSon成为“幻影需求”提前为此而分层实在不值得吧。其实就算你分了层以后切换的时候还是会有很多问题的,除非你的应用就像petstore一样简单。所以与其这么做,不如完善你的测试,如果变化来了,有测试做保障,提炼重构付出的代价不一定比你提前预防这种变化的的代价要大很多。而且一定要考虑这种变化的机率的大小。

分层的另一个好处是可以做孤立测试,说白了就是分离职责然后单独测试。但我前面说过分离职责不一定要分层的,你可以把业务复杂职责单一的功能放在另外一个类中。

我现在的做法是用spring web flow做控制层,不需要java代码,只要xml的flow定义,其中调用service层。这个service层负责事务边界也代理业务,直接使用JPA接口。domain对象除了jpa等元数据的映射,提供一些get方法(我是说计算方法,比如getTotalSize()),还提供一些验证逻辑。页面使用Rich Faces。

service测试大部分不做mock,而是连接数据库做测试,spring test框架支持自动回滚,可以用dbunit初始化测试数据就可以了。业务计算复杂的提炼出一个类来可以做mock测试了。

可能以后还想加上selenium做验收测试,现在还没有加。

icewubin 前天 回复
wangchao_17915566 写道

数据库+展现,剩下的一概不要


你这样不就是分了一层么?“展现”就是你的分层,你的展现如何实现?再分层么?呵呵。
czx566 前天 回复
wangchao_17915566 写道

数据库+展现,剩下的一概不要


不同意,除非你的业务逻辑都是单纯增删改查
wangchao_17915566 2008-12-01 回复
数据库+展现,剩下的一概不要
icewubin 2008-12-01 回复
引用
5. 如果对分层的体系结构熟悉的话,开发效率并不低(虽然代码量较大,但不复杂,易于调

试、测试和实现),可能有点boring,但更易于合作分工。

分层未必代码量大,完全是看你打算把什么东西分层,如何分层和分层的粒度问题。有时候更多的是逻辑上的分层,甚至于可以理解为编程规范,分得好或者说理解的好的话,不会增加多少代码和配置的。


xiaosi 2008-12-01 回复
说的很有道理
dicom 2008-12-01 回复
1. Domain Model是用来描述业务领域内的对象及其之间的关系的,目前的Domain model能够

做到这个,那就不能称其“贫血”。Domain Model应该是纯粹的,业务逻辑和持久化不应该是

Domain Model里描述的,业务逻辑属于Business Process Model, 持久化则是系统功能。

Domain Model不应该依赖DAO,是DAO依赖领域模型,Business Service则依赖Domain Model和

DAO

2. 分层,是把复杂系统进行分解成多个可把握的简化模块,由于系统本身还需要重新组合,

所以分层本身是将复杂系统更复杂了(增加了模块间的组合的复杂性和分层体系结构本身技术

复杂性),但各个模块简化了,易于理解把握和工作。

3. 分层本身适合按模块分工,和当前大多数按功能分工不相符。按功能或特性分工协作,要

求每个人都把握整个体系结构,分层增加了复杂性,但按特性分工不易整合。

4. 分层结构易扩展,适于大型项目(到底多大所大型呢?)

5. 如果对分层的体系结构熟悉的话,开发效率并不低(虽然代码量较大,但不复杂,易于调

试、测试和实现),可能有点boring,但更易于合作分工。
andycui 2008-12-01 回复
Dao要消灭的话,可以使用一个通用dao,而不要一个domain object一个dao,我们现在的项目就是,导致了太多的dao,或者干脆把dao的操作直接整合进domain object。

至于transaction的包裹,通过spring的话需要配置文件或者annotation,唉,其实我估计大家用的最多的就是spring的事务支持了,看看也就没啥了,以前的EJB早就提供了的功能,EJB3不也很好吗? 另外spring的事务支持团队里自己实现也是非常容易的,大家不要为了spring而spring。
netbaixc@gmail.com 2008-12-01 回复
robbin,大概看了那几个“血型”,嘿嘿,以前呆在国内一个知名公司里,平台是基于spring开发的,就要求分层,service里是描述事务和对外的门面,domain是针对业务的逻辑处理,dao是数据库操作,至于你说的domain object,是简单JAVA对象.当时的开发真是麻烦啊,尤其是要求不能跨层调用,就是command(响应http request)->service->domain->dao,而且这些都是通过spring的IOC配置在XML中,哇塞,那个配置文件看得人是头晕眼花,开发个CRUD那是一个痛苦啊,公司虽然提供了代码机来生成,可是也蛮麻烦的,让人忍不住想,这么搞有么价值?因为庞大的代码和配置文件,使得在开发和维护过程中需要阅读太多的东西了。虽然这些文件确实因为OO而变得职责单一,但是文件太多了,看起来也很费劲的!
说分层也好,说OO划分单一职责也好,不就是希望提高开发效率,利于后期维护么?但是如果分得太细,就会导致代码庞大;如果想要精简代码,有人说就得all-in-one。许多人在这个权衡上争辩,其实有一些更好的想法反而被埋没了,特此拍砖。
1.可以划分得越细越好,涨血到爆血,但是要伴随提供一个图形化的IDE,开发者使用IDE开发,将那么庞大的文件通过工具精简透明掉,开发人员在开发思路上仍然保持清晰的划分,但实际开发和维护通过图形化的向导啊之类的工具帮助来实现。现在很多中间件厂商都有提供哦。
2.采用元数据。记得有个技术公司的技术总监提点我:“小伙子,你看我们的项目,里面的代码不都是在描述业务么?什么校验,什么计算,什么查询”。其实可以将这些业务规则用纯数据来描述,按关系数据表来细粒度管理,然后通过一个框架式的代码来读取这些数据,进行“解析式”执行。这里的数据才是最精简的代码。

icewubin 2008-12-01 回复
czx566 写道

程序员最终要被智能编程消灭到


汇编被C取代的时候,也没见程序员失业啊。
china8jie 2008-12-01 回复
Why 消灭 Dao ??

那我们一直实践的东西是落后的? 噁待淘汰的 ?
czx566 2008-12-01 回复
先消灭 DAO

再消灭 View bean

最后程序员 只剩下 Business bean摆弄了
呵呵

程序员最终要被智能编程消灭到

upheart 2008-12-01 回复
抽象,抽象,抽象,一层,两层到N层
简化,简化,简化,N层,两层到一层
seven2seven 2008-12-01 回复
有点意思
didasoft 2008-12-01 回复
didasoft 写道

Robin能够简单解释一下:领域模型All-in-one指的是什么概念?


无法修改评论?写错字了。

Robbin能否简单解释一下:领域模型All-in-one指的是什么概念?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值