Grails消灭Dao带来的问题

Grails自己学了一段时间了。感觉快速开发的感觉很不错。以前要干好几天的事情,一天就能完成。不过Grails对领域对象持久化使用AOP的方式让对象自己持久化自己。比如new A().save()。
这样一来就给我们造成可以在控制器里很容易的CRUD,对数据库的访问随时随地都可以,而不用访问Service->Dao的方式处理。而且Grails自己产生的CRUD代码也都在控制器里进行了持久化操作。
对此我感觉很疑惑了。Grails、ROR这类快速框架的层次划分该怎么算?持久化操作该随时自己做还是仍然放Service里?
以前Dao可以看作持久化服务组件。我们通过应用层调用持久化服务。现在持久化服务给领域对象自己了。那么我们在哪里调用呢?是随时使用领域对象来调用还是专门写一个Service封装起来?

如果需要很多领域对象、服务一起工作,我当然会在Service里写。
传送参数到Service,Service调用服务、领域对象处理数据,最后保存并返回结果。
我主要疑问是最简单的CRUD,而不涉及其他业务的时候还需要到Service里吗?

语言的限制和框架的限制。
RUBY的model似乎自己持有一个连接,这在java里是需要被释放掉的。
正是因为在使用hibernate,所以不可能消灭掉dao,或许它会以另一种形式出现,做为范型基类来简化一些东西。
对于rails当然是要模型自己调用自己了,有血了。但是不要把相同的操作在service再实现一次,web会不知道它应该调用哪一个。
[该贴被freebox于2008-06-04 13:17修改过]

Grails确实消灭了Dao。Grails的领域对象也是充血模型。领域对象自己可以用Save、Find等方法自己查询访问自己。Grails只是把Hibernate作为自己的持久化实现GORM的底层。
所以我才要问,基本上只是最简单的CRUD,而不涉及其他事务和其他业务的时候,还需要Service层参与吗?控制器里自己就可以把自己保存或查询。

Service参与之后,web来调用哪一个呢?web会不知所措,web明明看见model是自管理的。所以我才认为对于它们不需要service。

是否使用service,取决于领域建模Evans DDD,而不是具体平台实现,在DDD中,service不是定义专门用来调用Dao的。

在DDD中也不是没有DAO,而是repository,仓储,我们获取一个冬眠的对象要从仓储中获得,因为仓储中储存的是我们的对象。

DDD认为领域对象应该是充血丰富的,但不是说save等数据库行为方法放到领域对象中就代表充血了,这些都是片面理解DDD。

领域对象中应该是丰富的业务行为,有时这种业务行为还会被我们用设计模式分离到其他类中,那么当前类就没有业务行为,但是你不能认为当前类就是失血模型,这是片面的,盲人摸象的。

将查询和save等行为放到对象内部,会产生悖论:一个对象能够自己获得自己吗?万事万物都有其母,孙猴子还从另外一方石头中蹦出来的,到了我们对象语言中,对象就可以找到自己了?创建自己了?这不是猫追自己尾巴吗?

可能是我没说明白吧?
传统Java开发一般分为表现、应用、领域和持久四个层。持久化的功能DDD里应该算服务吧,提供一个领域对象持久化的服务。传统上应用层调用领域对象、服务、值对象,通过这三者处理业务。Grails的GROM把这个服务给了领域对象自己。领域对象可以用类的静态方法查询该类的实例,领域对象的实例可以保存、更新自己。
这样就造成了我们可以在表现层、应用层都可以调用持久化服务。只要控制器或者Service里能调用到领域对象,那么就可以使用其持久化服务。
所以,在Grails简单的CRUD中,控制器里就可以完成持久化服务了。它自动生成的脚手架代码也是这么写的。比如你建立了一个领域对象,赋值后,直接调用其save方法就可以保存了。因为这么做和传统开发不一样了,感觉别扭,所以在这里发问。
[该贴被wlmouse于2008-06-05 13:18修改过]

一:我理解出来的意思是:他的业务逻辑就是简单CRUD,没有更多的内容。
是在这种业务逻辑情况下,是否还需要service。
在这里不是指复杂的业务逻辑。
当把复杂业务逻辑写在model里的时候,model会越来越复杂,所以我并不认为这是个好方式。

二:我认为持久化不是领域服务,算是基础服务吧。

>领域对象可以用类的静态方法查询该类的实例,领域对象的实例可以保存、更新自己。
前面我没说明白吗?这种方式根本就不是OO的嘛,不对的啊,哪有物体还不存在的情况下,自己找自己?逻辑悖论啊,领域对象可以冬眠 唤醒,但是不可以在自己还没有的情况下找自己啊,这不是疯子吗?我的意思不够明白吗?

>因为这么做和传统开发不一样了,感觉别扭,
无论什么框架和语言,当前都统一在Evans DDD思想指导下,凡是和这个开发不一样的就是歪门邪道。
[该贴被banq于2008-06-06 09:52修改过]
[该贴被banq于2008-06-06 10:01修改过]

BANQ现在有点偏执了

==BANQ现在有点偏执了
与其拍拍脑袋加入对象和对象方法属性,还不如这么偏执的选择DDD
DDD从细节上告诉我们如果划分对象如何构建业务层,没有什么方法比这个简单好用!

本来很是期待Grails,做了个性能测试和Spring一比,差距很大。
看来这个懒偷不得。

我觉得rails不认为model是单纯的实体概念。
看见的存取器或实例变量都是实体意义,而其它的find之类方法属于操控意义,把实体和仓储压缩在一起。

>与其拍拍脑袋加入对象和对象方法属性,还不如这么偏执的选择DDD
不是我偏执啊,Ruby on Rails当初诞生时打的旗帜就是Evans DDD啊,不信看看很多英文资料,作为ROR的翻版Grails如果舍本求末,忘记当初特点,不能说我偏执吧,我指出了这样一个事实本质,只是你们之前没有看到罢了。

当然,单从这点不能全部否定Grails,但是可以肯定的是,使用Grails之前必须掌握Evans DDD,这些rails兄弟就是为更好实现DDD诞生的。

个人不赞成在模型中加入find功能,那么关于save呢?在Java中我们可以使用模式来实现save,比如JiveJdon3加入的内部消息功能,当用户看完消息后,以后不再提示有新消息,也就是说,消息的内容被读取了,那么就更改消息的状态,我们可以使用观察者模式实现,如下,消息模型:



public class ToShortMessage extends ShortMessage {

public String getFilterMessageBody() {
this.shortMessageState.setHasRead(true, this);
return messageBody;
}

}

public class ShortMessageState extends Observable{

.....

public void setHasRead(boolean hasRead, ShortMessage shortMessage) {
this.shortMessage = shortMessage;
if ((hasRead) && (!this.hasRead)){
this.hasRead = hasRead;
setChanged();//出发
notifyObservers(this);
}else
this.hasRead = hasRead;

}

}
public class ShortMessageRepository implements Observer{
....
public void update(Observable obj,Object arg){
logger.debug(
" Observable update ");
ShortMessageState shortMessageState = (ShortMessageState)obj;
try {
this.shortMessageDao.updateShortMessate(shortMessageState.getShortMessage());
} catch (Exception e) {
e.printStackTrace();
}
}

}

getFilterMessageBody是在jsp输出消息内容时,被调用,由于ShortMessageState 的setHasRead是事件触发,导致ShortMessageRepository 的update被执行。

从这里看出:ToShortMessage 也包含了复杂的业务行为,不只是简单的Property get/Set,所以,不能认为ToShortMessage 是贫血模型,而且贫血模型也不是完全不好,特别在冬眠到关系数据库时非常简洁轻量。这又涉及另外问题,这里主要说明:不能教条主义看待失血和充血,JiveJdon3的ForumMessage粗看上去是失血,只有set/get,没有业务逻辑,但是看看其子包下MessagRender就发现业务行为不少,这些都是从FourmMessage中分离出来的,是细节设计考虑,当然如果为了面对形式上充血,我完全可以将MessageRender功能都并入ForumMessage,那谁能看得懂ForumMessage呢?OO宗旨又跑到哪里了呢?再说Evans DDD最终必须结合设计模式的。



[该贴被banq于2008-06-13 21:18修改过]