也就是说在领域模型中,本身只处理很少的行为逻辑,大多数都是处在模型上层的一系列的Service类来负责捕获处理这些领域逻辑行为。这违背了OO思想(OO认为应该把数据和行为合在一起)。
Eric Evans在他的Domain Driven Design中说过:
Application Layer [his name for Service Layer]: Defines the jobs the software is supposed to do and directs the expressive domain objects to work out problems. The tasks this layer is responsible for are meaningful to the business or necessary for interaction with the application layers of other systems. This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down. It does not have state reflecting the business situation, but it can have state that reflects the progress of a task for the user or the program.
Domain Layer (or Model Layer): Responsible for representing concepts of the business, information about the business situation, and business rules. State that reflects the business situation is controlled and used here, even though the technical details of storing it are delegated to the infrastructure. This layer is the heart of business software.
据说在Thoughtwork公司里面采用的技术是Hibernate+Spring+webwork,关于anemia domain model有不少员工在公司内部邮件中发问老马应该什么正确使用。但是老马还没有给出确切答案,只放入TODOList去。
我个人觉的Domail类中不应该含有过多的与之无关的行为,不但违反了单一职责的原则,还使得整个系统不够稳定。例如User中有Roles,那么addRole,removeRole自然应在Domain类User中写,至于removeUser,loadUser等应该封装成Dao去处理。
引用robbin在一次讨论中的回复:
我的理解是Martin大叔所谓的domain object是“领域模型”,它是一个针对业务逻辑抽象的模型,和软件编程根本毫无关系。即使你不开发这个软件,你仍然需要抽象你的业务逻辑得到你的领域模型。这个领域指的是你所从事的行业,而不是狭隘的持久化类。
并且领域模型的建模也是在需求分析阶段,或者在需求分析阶段之前完成的事情。具体到编程的过程中,领域模型并非对应某一个Java类,如果一定要强行对应的话,(业务对象,Dao接口,Hibernate实体类)他们合起来统称领域模型在Java语言的实现。把 商业建模范畴的“领域模型”拿来当做Hibernate编程中的实体类,根本就是牛头不对马嘴!
其实你的主张就是把持久化实体类的持久化操作附着到实体类上面去,而不是分开。举例来说,也就是说Account类的增删改查不应该单独分离到AccountDao接口去,而应该把增删改查操作放在Account类里面来完成,对不?
那么我在解释这个问题之前,必须澄清一点,就是这个问题的讨论,即持久化操作是否应该单独抽象一个DAO层的问题是和Martin Fowler提到了贫血的领域模型毫无关系的。实际上传统的Entity Bean就是这种把持久化操作附着到Entity Bean本身去的,但是Martin Fowler一样在说,这种Entity Bean就是贫血的。
好了,我们现在把其他无关因素排除了,focus到这个话题上,就是我们是否需要DAO接口的问题了。因为按照你的观点,如果把对象的增删改查都放在实体类上面,其实我们就不需要DAO接口层了,业务对象和Web Action都可以直接调用实体类本身来完成持久化操作了。
大概在2003年以前,我一直就是采用这种模型的,但是从2003年开始,我就改成了分离一个DAO层来专门处理持久化操作了。原因是多方面的,从技术角度来考量的话,分离就有很多好处,Rod Johnson在《without EJB》第10章持久化里面就详细阐述了需要DAO层的理由,我建议你看一下,这里不复述了。只谈一下除了Rod Johson提到理由之外的理由:
作为保持状态的实体类,他的职责是保持状态,而不负责状态的持久化。如果把持久化操作也放在实体类中,一方面来说,把两种不同的职责放在一个类中,并不符合OCP的单一职责的原则,然而更重要的原因是会带来实体类的不稳定的问题。
在我的理念中,实体类以及实体类关联关系是一个软件系统中最稳定不变的部分,在整个软件系统中,各个层面都需要涉及到实体类的操作,如何划分实体类,以及确定实体类关联是最费考量的,确定了这一点,整个软件系统就底层依赖关系就被确定下来了(实体类的属性可以变化,由于软件系统对实体的操作都是以实体类为单位的,所以实体属性的变化不会造成系统不稳定),上面的DAO层,业务层,Web层都只对实体类产生单向依赖。
如果你把DAO层合并到实体类中,请注意本来Web层是不依赖于DAO层的,Web层只依赖实体类(因为它要展现实体类状态),但是由于你合并了,使得Web层也变得依赖那些DAO层的操作了。这样的结果就是让软件系统的耦合性提高,可伸缩性降低,可维护性降低。
DAO层的变动是大于实体类变动的,实体类基本上稳定不变,而软件系统的需求变更几乎一定会带来DAO层的变动,但是并不会带来实体类层的变动(会带来实体属性的变动,但是很少会影响到实体类本身)。所以DAO层的变动频率远远大于实体类。那么当你把DAO层操作合并到实体类以后,其结果就是让实体类的变动频率等于DAO层的变动频率。那么就会造成本来稳定的实体类层变得变得频率高了很多,而实体类的变动会波及到软件系统的每个层面,造成软件大面积的相关性和不稳定。
请记住很重要的一点:实体类是有状态的类,DAO类是无状态的类,在整个软件系统中,只有两种类有状态,一个是实体类,一个是HTTP Session。有状态的类会带来很高的代码相关性,应该尽量减少有状态类的影响范围,尽量减少有状态类的变动频率,应该尽量减少有状态类的职责。
而你把DAO操作合并到实体类以后,结果就是扩大了有状态类的代码相关性的范围和影响范围,扩大了有状态类的变动频率,最后就造成软件系统的稳定性下降。