论坛首页 Java企业应用论坛

一个简单例子:贫血模型or领域模型

浏览 77067 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-12-04   最后修改:2008-12-04
pf_miles 写道
to 楼主:

关于贫血模型的缺点:
引用
“# 将所有的业务放在无状态的service中实际上是一个过程化的设计,它在组织复杂的业务存在天然的劣

势,随着业务的复杂,业务会在service中多个方法间重复。
# 当添加一个新的UI时,很多业务逻辑得重新写。例如,当要提供Web Service的接口时,原先为Web界面

提供的service就很难重用,导致重复的业务逻辑(在贫血模型的分层图中可以看得更清楚),如何保持

业务逻辑一致是很大的挑战。”


这两点我不太赞同,说“业务会在service中多个方法间重复”这可以通过即时的重构解决,如抽取private方法,甚至在“多个service之间重复”时可以抽出包含共用逻辑的类;

第二句话“service就很难重用”我觉得也说不过去,贫血模型中的service是无状态的,可以纯粹看做一段组织好的代码,别的什么地方要用这个逻辑,直接将这段代码inline调用即可,不必担心任何状态相关的问题;总之要在webUI和远程接口之间共用某个service是肯定可以的。

service里的业务逻辑代码会越来越长我赞同,并且正亲身体验中;尤其对于变化很快的某个行业的逻辑;
但是别忘了,上了一定规模的系统,目前还是很少采用领域模型的方式来组织的,原因如楼主描述的“领域模型的缺点”,其实别看小小的几句话,里面涵盖的风险是很大的,特别对于大型系统;或许还因为目前的大型系统一般建立地比较早,而采用了早已成熟的贫血模型,由数据库驱动而来的系统显然贫血模型更加自然,开发人员也好找。

第一个问题,重构能够解决部分问题,但不能解决全部问题。我承认对于这个简单的转账例子,如果添加取钱和存钱服务,一个优秀的程序员完全能够提取方法重构来消除代码上的重复,但是请注意,这只是技术上的手段,不是领域的驱动。我曾经参加的一个项目,当我查看现有代码时,发现里面很多重复代码,由于看完《重构》不久,十分想练手,SDM也赞同,于是让我做code review。刚开始很兴奋,但是当消除几个明显的代码重复之后,剩下来的工作越来越举步维艰,很多逻辑相互交织在一起。虽然明显看到其中存在重复,但是却很难使用提取方法来重构,好几次还不得不回复成原来的代码,因为重构后的方法和原来的代码存在细微的不一致。提取后的方法很少具有业务意义,它只是技术上的手段,因此也并有因为重复代码的消除而获得更可理解的代码。对于这种过程化的类,我发现几乎仅有的重构手段就是提取方法,不管是提取到当前类,还是提取到一个外部的工具类。最终的结果是,我的重构实质上并没有带来实质性的好处。我认为,重构需要有领域模型的引导,否则它可能走错方向

 

第二个问题,你认为WebUI和WebService接口的Service代码能够重用吗?两者接口的粒度不同,这会导致它们使用的DTO完全不同,要想重用几乎是不可能的。关于我谈到的领域模型的缺点,我坦率的承认实施领域模型有很大的风险,尤其当项目中没有一个人真正懂得领域模型时候。

 

 

 

 

 

pf_miles 写道
to taowen:

引用
“另外,Repository只应该负责Aggregate Root。对于被Aggregate的对象,应该用Navigation,也就是在

关系之间游走来获取。所以不是所有的查询都必须由Repository来完成”


这么说来repository只用来取root?从概念上讲不错,可是要知道往往一个root下面会有成百上千个node,这么巨大的一坨关系树我们用 repository取出来然后在“关系之间游走”...是否有点滑稽?呃...或许我们能想到引入延迟加载,取出来的root下的二级node都用 stub来占位;不过这又为污染我们纯洁的领域模型创造了良机...
我想你肯定解决过这类问题,能否多介绍一点?

 我来回答这个问题吧,对于Aggregate Root的概念你可能理解有问题,详细介绍还是请参看Eric Evans的《DDD》,我这里只举一个简单的例子,订单的例子(又被用滥了)。关于Order, LineItem,Product的关系,这里不用说了吧(我讨厌画图),对于Order我们需要单独跟踪它,因为客户有可能去查询某个订单(根据订单号),因此它是一个聚合根。但是对LineItem,我们一般不需要跟踪它,凡是当我们谈论一个LineItem时它必定要和一个Order联系,也就是说有个Order的上下文在,因此LineItem不是一个聚合根,它属性Order这个聚合根。同样我们也需要跟踪Product,我们需要查询所有的Product,需要获得单个Product的信息,因此Product也是一个聚合根。在这里有两个聚合根,Order和Product。按照你的观点(我揣测的),可能认为只有Order这一个聚合根,这是错误的。

1 请登录后投票
   发表时间:2008-12-04  
看过帖子,受益啊,学啦
0 请登录后投票
   发表时间:2008-12-04  
好像我目前做的 都是 “贫血模型”(自己还不怎么知道这么一个概念,汗!!!)
关于:
1.所有的业务都在service中处理,当业越来越复杂时,service会变得越来越庞大,  最终难以理解和维护。
2.将所有的业务放在无状态的service中实际上是一个过程化的设计,它在组织复杂的业务存在天然的劣势,随着业务的复杂,业务会在service中多个方法间重复。
3.当添加一个新的UI时,很多业务逻辑得重新写。
例如,当要提供Web Service的接口时,原先为Web界面提供的service就很难重用,导致重复的业务逻辑(在贫血模型的分层图中可以看得更清楚),如何保持业务逻辑一致是很大的挑战。
    这几点提出的太完美了。特别是1,2点,我特别有这种感觉。(面向过程)

0 请登录后投票
   发表时间:2008-12-04  
看的有这种感觉:
      对领域模型有种似曾相识的感觉,在哪里呢 ?puremvc
     我对这个也做了少许学习,在flex方面。发现他们的观点很相似,比如业务处理不仅仅只放在action中 ,同样在module数据层方面也做与之相关的业务处理。在view方面也做它的相应处理。刚开始就觉得这种模式非常不错。
    
0 请登录后投票
   发表时间:2008-12-04  
我也说一句:

    面向对象不是万能的,计算机处理任何有个事情,其实都有一个过程调用。   
    那么在利用充血模型实现时,那么一个业务中的过程调用职责就是service层存在的意义, 而被过程调用的单元元素的职责 都应该放入到领域模型中。

    这样是最理想的了。


最后谢谢楼主,受益良多!
0 请登录后投票
   发表时间:2008-12-04  
现在面对对象数据库正在兴起, 如果持久化使用oo数据库, DAO是不是就可以扔掉了?
0 请登录后投票
   发表时间:2008-12-05  
teclogid 写道
fnet 写道
terranhao 写道
新手问一下,不用DAO,假如我要条件查询怎么解决?
比如select * from entity as e where e.name=:name and e.id=:id
写在service里面?



放在Service里面也就一句连写

如果我多个service都用这个查询呢?如果这个查询某天需要修改呢?修改所有service?bad smell。



用设计模式解决,不用我提醒了吧
0 请登录后投票
   发表时间:2008-12-05  
ray_linn 写道
看了你的代码,有个感觉是无论是DAO或者是Resposity,都是domain logic里的噪声。如果Domain logic也能透明的调用DAO或Respoisty,这样的domain模型会更完美,而且更关注业务。


同意。 respository 应该尽量淡化基本存储逻辑,而主要负责查询(find,query*)
0 请登录后投票
   发表时间:2008-12-05  
qufulin 写道
现在面对对象数据库正在兴起, 如果持久化使用oo数据库, DAO是不是就可以扔掉了?



正在兴起?? 面向对象数据库早就基本死掉了

看这个:  http://www.infoq.com/cn/news/2008/11/Database-Martin-Fowler
1 请登录后投票
   发表时间:2008-12-05  
讨论必然是无结果的。。

回过头来想一想分层的目的吧。是为了什么才做的分层。别忘了本阿。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics