这是 domain driven model design该如何设计的几个连接 :
http://gigix.blogdriver.com/gigix/166013.html 、http://gigix.blogdriver.com/gigix/168486.html
http://gigix.blogdriver.com/gigix/168634.html 、http://gigix.blogdriver.com/gigix/169972.html
现将其精华谪录如下:
Martin Fowler更倾向于Domain Model模式(http://www.martinfowler.com/eaaCatalog/domainModel.html)。领域对象不仅是原始数据的对象形式封装,而且包含几乎所有重要的业务逻辑。领域对象可以直接访问数据库,因此DAO不是必要的,而是通过一个他称为 “mapper”的东西提供O/R映射。由于领域对象封装了业务逻辑,service只是一个很薄的facade,提供面向服务的接口。Spring的 JPetstore范例就是按照这种方式架构的。但是,JPetstore的业务逻辑相当简单,我草草看了一下,似乎领域对象没有直接操作持久化逻辑,因此这并不是一个很有说服力的例子。或许更有说服力的是Pluto(http://jakarta.apache.org/pluto/)。在Pluto这里,需要持久化的大多是些配置信息(包括portal本身的配置、portlet的配置和用户配置),它并没有使用DAO,而是直接通过XML binding持久化到XML文件。
我不知道应该如何选择这两个体系结构模式。我不知道Transaction Script模式的缺点在哪里。我不知道Domain Model模式用起来会有什么困难。我不知道Martin Fowler为什么如此激烈地批评“贫血的领域对象”。这让我感到困惑和苦恼。我要首先再看一遍PoEAA的相关章节,再想想,再问问。我不喜欢感觉自己像个白痴。
。。。。。。
评论人:charon
2004-05-23 15:08:51
评论人:potian
2004-05-22 13:49:04
在分布式程序中的一个副作用是提高效率(通过DTO)
- 评论人:partech
2004-05-22 11:53:35
服务层本质上是facade,确实不应该包含业务逻辑。
- 评论人:partech
2004-05-22 11:24:22
你们的做法我说一个缺点,不知道你们的应用同参与者是否只有一种交互形式?比如只通过WEB。然而参与者进行业务可能有很多种方式,拿银行来说吧,可以通过柜台,也可以通过ATM,还有电话,和网上银行。很明显这些应用后台都需要服务,而且这些服务的交互方式是不同的,也就是用例不同。所以Service层会不同,DTO会不同。但是如果我在这些应用中都进行转账业务,那么不管我使用的是哪一种途径,负责转账的代码都该只有一份。Service层的代码没多少复用价值,真正需要考虑的是Domain层的复用。
领域对象不单包含实体对象,还应该包含控制对象。很明显转账的整个操作没法划给单个对象,但是分解出的转出和转入操作还是可以作为帐户实体的方法的。
不过我感觉有人常常将领域层的控制对象同应用层的服务对象混淆。区分两者很简单,一个是不针对特定应用的,一个是针对特定应用的,也就是系统中烟囱的部分。
服务层充当的是系统中烟囱的部分,所以在不同应用中不考虑复用。或者说尽量避免进行复用,这样各应用就可以独立演化了。
可以看看下面的网站,作者的书里提到了很多如何进行领域层开发的方法。
http://domaindrivendesign.org
。。。。。。
下午Jacques Lebrun帮忙找到了Domain Driven Design,还没来得及看。Martin Fowler说应该由Domain Model负责访问持久化逻辑,并借助Data Mapper封装持久化的实现细节。但是,我竟然找不到这样的一个例子。Spring JPetstore细看之下原来也是Transaction Script,只不过service的方法都非常非常简单,前天乍一看还以为和我们做的Transaction Script有什么不同呢。看JPetstore唯一的收获是:不要使用DTO,用轻量级持久化框架(例如Hibernate或者iBatis),直接把实体对象到处传。
Domain Model模式看了仍然是半懂不懂,很多细节上的问题想不清楚。比如说,领域对象(或者说,实体)的创建和查找(对应于DAO的create方法和 load方法)由谁来调用?仍然在service里调用吗?如果要放到实体类,在创建/查找操作之前,没有一个具体的实体对象存在的,那么就只能放在一个 static方法里了,这是一个好的做法吗?
到哪里可以找到一个Domain Model体系结构的例子?我想看看究竟应该怎样去做。如果到明天晚上还没有进展,我就写信去问Martin Fowler本人。
。。。。。。
评论人:Windy
2004-05-24 20:09:22
- 评论人:partech
2004-05-24 13:31:58
评论人:potian
2004-05-23 20:14:27
但当模型复杂的时候,Service就需要通过模型来完成工作,或者从Service得到合适的领域对象,直接利用这些对象的行为进行操作 我在绍兴、兰亭玩
- 评论人:gigix
2004-05-23 20:05:35
- 评论人:potian
2004-05-23 19:59:25
而事实上没有一个纯粹的什么Domain或者纯粹的Transaction Script,(我对这些名词越来越讨厌了),很多时候需要混合, 譬如service-》model-》〔Manager〕DAO,或者是直接的service-》manager-》DAO
。。。。。。
Potian几句话说得我好象有点开窍了。原来,采用什么体系结构模式取决于我的实体(领域对象)干什么、我要持久化什么。如果像Pluto、 OSUser,领域对象本身有复杂的功能,持久化的是一个功能组件的状态,Domain Model就比较合适。而我们做的很多web应用,实体代表的是简单的数据(比如“用户”,甚至是某种公文),业务操作大多是对领域对象的CRUD操作, Transaction Script就挺合理。我感觉桌面应用和这种web应用好象有点区别,原来就是这个区别:它们的业务操作是做了不同的事。
。。。。。。
评论人:potian
2004-05-24 04:58:27
- 评论人:potian
2004-05-24 04:34:04
如果是领域模型比较复杂,那么完全通过Service就可能导致Service非常复杂,这个时候Service层就作用不大了。我们会提供一些简洁的Service方法来获取不同的领域对象,然后把领域对象直接暴露给客户端使用。
还有一个影响使用何种Service的情况就是客户端的状态问题,假设我们的领域对象比较复杂,但客户端和领域对象处于不同的JVM,那么一种方式利用 Remote对象存取领域对象,例如EJB里面的远程对象,这种方式可以直接操作领域对象,但是却会造成性能上的问题,所以就会产生DTO这样的模式。
- 评论人:potian
2004-05-24 04:09:02
第二种方式,service的主要作用是能够新建和获取(包括查询方法,通常用来统计和查询跨领域对象的数据和对象
)到领域对象,绝大部分的操作度直接在领域对象上进行,坏处是需要知道很多后面对象的接口和行为,但好处是客户端的变化通常不影响到服务端的变化。
- 评论人:potian
2004-05-24 03:24:32
在Web 应用程序中出现很多疑惑的原因是HTTP的无状态,在一个桌面应用程序中,我们可以一直在客户代码中持有这个领域对象,不断地对这个对象进行操作,但在 Web应用程序中,这个就很难,因此通常必须依赖service来进行“一记头“的操作。传递的数据通常是简单的数据类型或者是简单的数据类型组装起来的 Bean对象。
而WebWork"官方“宣称有两种方式,一种把Action作为简单的控制器,这是“传统“的方式,这种方式就可能直接导致必须使用service,而存取的数据通常是原始类型的数据。而另外一种方式则是所谓的领域丰富的Action,因为Xwork的ognlstack和 Xwork的action不需要象struts一样依赖于任何其它(例如serlvet),因此可以(在Action方法或者在Interceptor) 中通过Service得到领域对象,然后直接在界面上存取领域对象,这种方式比较象传统的桌面应用程序。另外Xwork可以直接把界面上的简单数据类型转换为对象。
。。。。。。
名称:
用Domain Model替代Transaction Script
意图:
你的业务层采用Transaction Script模式实现
你希望将实现方式改为Domain Model模式
Bad Smells:
- Long Method / Large Class——由于放置Transaction Script的service组件涉及全部业务逻辑的实现细节,其方法很容易变得冗长;如果对其使用Extract Method重构,则会使service实现类变得庞大。
- Duplicate Code——不容易从现有的Transaction Script中找出可复用的代码,因此常常重复实现类似的逻辑。
- Switch Statements——在领域对象有继承关系时,Transaction Script需要对不同类型的领域对象做不同的操作,此时容易造成基于类型的switch语句。
- Data Class——领域对象只携带了数据,作为对象的职责不够。
重构过程:
- 使用Extract Method,消除Transaction Script中的重复代码。
- 使用Move Method,将领域对象固有的业务逻辑移到领域对象类中。
- 如果该方法需要使用DAO,则将DAO作为参数传入。
- 如果该方法需要使用其他service,说明该方法并不适合存在于特定领域对象类,而应该仍然存在于service组件中。
- 如果领域对象是一个继承体系,将业务逻辑移到其基类中。
- 如果第2步搬移的方法中有switch语句,使用Replace Conditional with Polymorphism消除之,将业务逻辑分配到领域对象的各个子类。
。。。。。。
评论人:gmark
2004-05-26 11:26:16
- 评论人:UltraFool
2004-05-25 20:54:52
评论人:Ronald Matt
2004-05-25 11:02:25
这么Cool的代码是重构不出来的,因为重构只有Code Smell作为输入。
那究竟漏掉了什么?呵呵。
- 评论人:iseeisee
2004-05-25 10:48:19
or:
service - domain - dao
- 评论人:iseeisee
2004-05-25 10:12:27
- 评论人:透明
2004-05-25 09:52:08
- 评论人:iseeisee
2004-05-25 09:44:07
我觉得dao也没必要注射了,直接全局访问
ServiceLocator.getXXXDao()
>>如果该方法需要使用其他service???
假设
1.
UserChanagePasswordService...
changePassword(String username,String password){
User u = UserManager().getUser(username);
u.changePassword(password);
}
User...
void changePassword(String password){
_password = password;
ServiceLocator.getUserDao().save(this);
ServiceLocator.getMessageService().send("成功修改密码:"+password);
}
2.
UserChanagePasswordService...
changePassword(String username,String password){
User u = UserManager().getUser(username);
u.changePassword(password)
//_messageService可以注射进来,
_messageService.send("成功修改密码:"+password);
}
User...
void changePassword(String password){
_password = password;
ServiceLocator.getUserDao().save(this);
}
呵呵~~~~,也分不出哪个更好些,
甚至可以:
user.addPasswordChangeListener(xxxxxxx);
不过这太难了,不好吧??
- 评论人:moxie
2004-05-25 09:11:19