论坛首页 Java企业应用论坛

Domain Object贫血vs富血(DDD)和spring roo到ruby的扯淡

浏览 42407 次
该帖已经被评为良好帖
作者 正文
   发表时间:2011-04-16   最后修改:2011-04-18
引子:
前几天,小胖和我说他们公司CTO批他了,说他写的代码不够OO,不够DDD。细问才知道他们CTO在推DDD(领域模型驱动设计).我当时给他的观点是,JavaEE应用是天生贫血的,并不能像ruby之类的语言做到很好的富血,做到DDD。因为这些观点也是N年前讨论过的问题,我记得冒似robbin当年还下过定论:Java天生是贫血的。所以有了ruby之流做RAD快速开发。但当seam到spring roo的出现与完善,富血DDD在Java里也变得可行起来(此论言之尚早,拭目以待)。我以前也和别人争吵过哪个更好,现在我的思想又受到了一些冲击,你呢?世界在发展,我们的思想是不是也应该与时俱进呢?

贫血vs富血

我们来回顾一下。在企业架构模式中,业务层的实现一般有两种模式:一种是事务角本模式(Transaction script),另一种是领域模型模式(Domain Model)。这两种分别对应贫血和富血。好吧,我们不说这些扯淡的东西,我们简单点说。

所谓贫血,就是一个对象里只有属性,如java中的pojo,要实现业务,要依靠service层来实现相关方法,service层的实现是面向过程的,也就是所谓的transaction script.我们现在大量的分层应用action->service->dao->entity的方式就是这种贫血的模式实现。

//贫血代码示例:
Account.java
Public class Account{
	private String name;
	private Long num;
    get set…

}

AccountService.java
Public class AccountService{
	public List findAccountBySomething(String abc){}
	public void addAccount(Account a){}
	public void  otherBiz(){}

}


所谓的富血就是属性和方法都封装在Domain Object里,所有业务都直接操作Domain Object。Service层只是完成一些简单的事务之类的,甚至可以不用service层。也就是直接从action->entity.

//富血代码示例
Account.ava
Public class Account{
	private String name;
	private Long num;

	public List findAccountBySomething(String abc){}
	public void addAccount(Account a){}
	public void  otherBiz(){}
}


前人总结的一些贫血和富血的对比:
贫血模型的优点:
1.被许多程序员所掌握,许多教材采用的是这种模型,对于初学者,这种模型很自然,甚至被很多人认为是java中最正统的模型。
2.它非常简单,对于并不复杂的业务(转帐业务),它工作得很好,开发起来非常迅速。它似乎也不需要对领域的充分了解,只要给出要实现功能的每一个步骤,就能实现它。
3.事务边界相当清楚,一般来说service的每个方法都可以看成一个事务,因为通常Service的每个方法对应着一个用例。
其缺点为也是很明显的:
1.所有的业务都在service中处理,当业越来越复杂时,service会变得越来越庞大,最终难以理解和维护。
2.将所有的业务放在无状态的service中实际上是一个过程化的设计,它在组织复杂的业务存在天然的劣势,随着业务的复杂,业务会在service中多个方法间重复。
3.当添加一个新的UI时,很多业务逻辑得重新写。例如,当要提供Web Service的接口时,原先为Web界面提供的service就很难重用,导致重复的业务逻辑(在贫血模型的分层图中可以看得更清楚),如何保持业务逻辑一致是很大的挑战。

富血的优点是:
1.领域模型采用OO设计,通过将职责分配到相应的模型对象或Service,可以很好的组织业务逻辑,当业务变得复杂时,领域模型显出巨大的优势。
2.当需要多个UI接口时,领域模型可以重用,并且业务逻辑只在领域层中出现,这使得很容易对多个UI接口保持业务逻辑的一致(从领域模型的分层图可以看得更清楚)。
富血的缺点是:
1.对程序员的要求较高,初学者对这种将职责分配到多个协作对象中的方式感到极不适应。
2.领域驱动建模要求对领域模型完整而透彻的了解,只给出一个用例的实现步骤是无法得到领域模型的,这需要和领域专家的充分讨论。错误的领域模型对项目的危害非常之大,而实现一个好的领域模型非常困难。
3.对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。
4.对于事务等的处理,如果完全DDD,java支持得不够好。

关于Spring roo
引子中小胖公司就是采用Roo完成DDD富血开发。Spring Roo is a popular open-source rapid application development (RAD) tool for Java developers. ,使用命令行或工具操作来生成自动化项目,操作非常类似于rails。Roo可以很好的支持富血的Java DDD。关于roo我也不想说太多,因为我也没有亲自实战过,大家可以google一下。

如果采用roo,只需要
对于一个业务实现
@Entity  
@RooEntity  
@RooJavaBean  
@RooToString  
public class Employee {   
    @NotNull  
    @Size(max = 200)   
    private String name;   
  
    @Temporal(TemporalType.TIMESTAMP)   
    private Date birth;   
}  

//EmployeeController代码如下: 
@RooWebScaffold(automaticallyMaintainView = true, formBackingObject = Employee.class)   
@RequestMapping("/employee/**")   
@Controller  
public class EmployeeController {   
} 


在这里出现了一行@RooWebScaffold注解,做过rails的人会想,这不是rails里面的scaffold吗?没错,通过@RooWebScaffold注解,EmployeeController自动获得了curd的所有功能。

好了,就这么简单,一个domain,一个Controller,我们就可以发布到tomcat中运行了。

引用
Roo功能目前还不很强大,比如还不能根据配置的数据库链接直接从数据库表来生成Entity,以及前端表示层使用JSP和绑定Spring MVC(基于Spring 3.0支持REST)等,但是这些改进目标已经纳入其roadmap中。说一下另一个框架Seam对于富血也做得不错了,但还不是完全富血,同样也绑带了JSF。

这两天又看了一下spring roo,修正一下观点,新的spring roo版本已经可以用DBRE reverse from db.并且好像也很强大。

1.一个entity只需要写属性就行,get set免。
2.简单的增删改查功能,一键生成。
3.通过aspectJ实现编译时aop,完全不影响性能。
4.可以动态查询:如findPersonByNameAndMinMax(),相当于like name,between min to max这样的功能。
5.用Spring roo实现DDD,只有两层Controller层和Entity层。另外Service层(可选),Dao不推荐用了。
6.jms,email,json序列化支持
7.其它说明:开发需要JDK1.6以上。
8.不想用roo时,可以快速删除annotation和相关的jar包等文件。
...

Spring roo架构overview:




AspectJ实现编译时AOP




自动动态查询,自动生成





想了解更多roo内容,可以看看此文:使用Spring Roo ,感受ROR式的开发
http://www.iteye.com/topic/443059
还有看官方文档:
http://www.springsource.org/roo

关于ruby
Ruby我没有使用过,只是有所了解。所以不敢评价。但是当spring roo等框架成熟起来后,java语言就能很好的支持富血,更好的OO,更好的DDD,也能支持web快速开发了。那么我们能不能斗胆问一句:基于Java的开发者,想实现ror一样的web开发,还需要ruby吗?我们没必要向ruby转了吧?


小结:
我们对于新的东西,总是会有一种天生的阻抗性。就如我习惯了贫血的开发模式,有了更OO的spring roo出现时,我内心里还是不大看好它。正如前些天一些人说spring越来越庞大,不好一样,我想他们也是内心的阻抗性在起作用。但一个人要有一个更高的视野,时刻准备一些东西,当风暴来临时,可以从容面对

ps:说一下我的观点:
我是多年的贫血模型的实践者,基本上并没什么特别觉得不适的地方,虽然贫血模型不那么OO。其实我对于这个spring roo并不是特别看好。但是需要去了解它,和关注它。(可能又要修正了,看发展情况吧)

国外有一篇文章对spring roo的观点,我是比较赞同的:
When to use Spring roo?
http://java.dzone.com/articles/when-use-spring-roo?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+javalobby%2Ffrontpage+(Javalobby+%2F+Java+Zone)
我抽出几个重要的出来:
What is Spring Roo?
“Spring Roo is a lightweight developer tool that makes it fast and easy to deliver instant results. Best of all, you code 100% in Java and get to reuse all your existing Java knowledge, skills and experience.

Spring Roo is awesome for CRUD-Clients!

Spring Roo is good for learning Technologies!

Spring Roo is NOT good for complex Projects (yet)...

Conclusion: Spring Roo is a nice Tool => Become a Part of the Community!
My final conclusion: You should know Spring Roo, because it is nice, but you should also know when to use it and when to use something else. Use it to create CRUD applications or to learn technologies. Do not use it (yet) for complex, large projects.

基本上结论就是,你必需知道spring roo,因为它确实很好,但是你必需知道什么时候使用它,什么时候使用其它的开发方式。用它来生成简单的,业务不复杂的crud应用,或者只是用来学习新技术。不要使用spring roo来做复杂的,大型的项目。

别人的一些观点:
1.Martin Flowler批贫血的文章:AnemicDomainModel
http://martinfowler.com/bliki/AnemicDomainModel.html

也许在现有的技术和框架支持下,Java的DDD和富血应用已经开始成熟。ruby是否可在java界休止了呢?等待先行实践者分享经验。

欢迎拍砖,谢绝谩骂!

附件为:spring roo快速学习文档
  • 大小: 37.3 KB
  • 大小: 39.3 KB
  • 大小: 87.3 KB
  • 大小: 87.3 KB
   发表时间:2011-04-17  
引用

3.对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。



我倒不觉得把两者过于绝对化——非要弄个势不两立的态势不可——然后做出了胜利的二选一才是真正的结论。


我感觉这无非是一些设计思路而已,各有市场。
0 请登录后投票
   发表时间:2011-04-17  
引用
Ruby我没有使用过,只是有所了解。所以不敢评价。但是当spring roo等框架成熟起来后,java语言就能很好的支持富血,更好的OO,更好的DDD,也能支持web快速开发了。那么我们能不能斗胆问一句:基于Java的开发者,想实现ror一样的web开发,还需要ruby吗?我们没必要向ruby转了吧?

我觉得spring社区推出roo,有保留java开发人员,进一步打击ruby的意义。
0 请登录后投票
   发表时间:2011-04-17   最后修改:2011-04-17
贫血富血与service、dao等没有必然联系。

service一般是被视为面向接口设计和facade模式,所谓“服务层”,意义是很清楚的,只是提供业务逻辑层的统一包装和调用,这一层与DDD也没有任何冲突。DAO也如此。
service可以deleagte DomainObject来实现具体功能,DomainObject也可以通过切换DAO impl来改变自己的持久化策略。
贫血对象、涨血对象(提到这个我就想起蚊子)的本质是对象是否除了数据还能有行为。
在贫血对象模式下,对象的行为被提出,一般用DAO,Service来分别分流对象的持久化和功能封装,而更重要的是DAO,service往往不是设计为只针对一个Object,这就轻易解决了对象的职责不容易理清的设计难题。
service其实也没有排斥DDD,你完全可以在service中调用富血对象完成具体业务逻辑。


1 请登录后投票
   发表时间:2011-04-17  
不建议使用在大型系统中使用自动化代码生成工具,不管是自己开发的还是开源的,自己开发的代码生成工具还好,开源的出了问题就麻烦了。开发的时间节省了,维护的时间却大大的增加了。
0 请登录后投票
   发表时间:2011-04-17  
除非能够抛弃关系型数据库,否则对此不抱乐观。ORM从某种程度上说,并没有取得成功,OO的视角是流转的数据,而DB的视角是数据的流转,这种矛盾从架构上是不可调和的。除非能够在业务系统中淡化数据库的角色(隔离它),或者干脆用新的持久化方法(取代它)。原因还是和上面说的一样,数据库的访问方式决定了它的侵入性。
0 请登录后投票
   发表时间:2011-04-17  
让一个Domain Object承载所有的逻辑方法就是OO?让Domain Object的属性上承载那么多Annotation就是OO?现在所谓的CTO,程序都没写过几行就在那边胡扯什么OO,一个没写过超过1000w行代码的人,我认为连说OO的资格都没有。

任何的开发方法和模式,都不能和程序开发中最基本的最佳实践相违背。在Java中,让Domain Object和Service Layer过度膨胀都是错误做法。只有两者结合,才能写出可维护的代码来。程序员在这里需要权衡的,是哪些逻辑应该放在Domain Object里面,哪些逻辑应该放在Service Layer中。
0 请登录后投票
   发表时间:2011-04-17   最后修改:2011-04-17
我有三点疑问:
1、同样当业务逻辑变得越来越复杂时,富血的领域对象也将变得越来越复杂,难以维护,如何解决?
2、在领域对象中封装了太多的方法或属性,是否有必要,可见性等等会不会造成更加混乱,如何解决?
3、还有这样的领域对象是否适合做DTO呢?
这样理解第二点疑问:
public class clsA{
method1();
method2();
method3();

}
public class clsB{
//需要调用method1,但不能调用method2、method3
}
public class clsC{
//需要调用method2,但不能调用method1
}

0 请登录后投票
   发表时间:2011-04-17  
引用

3.对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。


我到觉得简单的软件用领域模型更方便点。
0 请登录后投票
   发表时间:2011-04-17   最后修改:2011-04-17
downpour 写道
一个没写过超过1000w行代码的人,我认为连说OO的资格都没有。


你是不是多打了一两个零?
这个世界上有多少人能写过超过1000w行代码?
0 请登录后投票
论坛首页 Java企业应用版

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