论坛首页 Java企业应用论坛

再乱弹一下“领域模型与数据访问接口的依赖问题”

浏览 22721 次
该帖已经被评为精华帖
作者 正文
   发表时间:2005-09-15  
robbin 写道
To firebody:
软件的业务模型在很大程度上要受到技术发展水平的制约。在O/R Mapping工具流行以前,
甚至连贫业务的领域模型也无法在项目中使用。随着O/R Mapping工具的流行,我们开始强调领域模型也放入业务逻辑。
但是正如你提到的,我也承认,O/R Mapping下面的领域模型仍然是贫业务的领域模型。
这倒不是因为我们不愿意放入更多的业务逻辑,而是受到技术发展水平的制约。
具体来说,就是受到了数据库资源打开关闭和应用事务管理横切面的极大制约。
如果技术的发展可以突破这种制约(例如更加智能,完全透明化的持久机制和全自动的资源管理和事务管理),
我相信实体领域模型(用代码实现的领域模型)会更加富含业务逻辑,会更加贴近概念领域模型。


Robbin可以仔细读一下PoEAA的相关章节或是Core J2EE Parrerns,根本就没有这种发展水平的制约,
O/R Mapping被这样用了是对O/R Mapping的浪费和误用,变成了和.NET的DataSet一样的东西。

列个简单的关于User类领域模型伪代码:
// 由于表现层不能直接访问业务模型,需要一个Facade,在这个Facade上作事务管理,初始化/结束持久工作单元的边界。
// 这个Facade没有业务代码,直接转发给BizObject。这是一个单例。
public class UserService {
    @PersistenceContext EntityManager _em;
    
    @Transaction
    public void createUser(UserVO userVO); {
        // VO不含对象关系图,只有accountId之类简单字段,而不是一个AccountVO字段
        new User(userVO);;
    }

    @Transaction
    public void disableUser(String userId); {
        User user = _em.load(User.class, userId);;
        user.disable();;
    }
}


// 业务类,不能由表现层直接访问。
public class User {
    @PersistenceContext EntityManager _em;
    
    public void User(UserVO userVO); {
        setName(userVO.getName(););;
        setEmail(userVO.getEmail(););;
        ...

        // 注册到工作单元,一般可实现由环境自动注册。代码类似于_em.persistNew(this);;
    }

    public void disable(); {
        for (Account account : _em.find(Account.class, "userId = ?", this.id();););
            if (account.isActive(););
                throw new IllegalStateException("Disable user error: Has active accounts.");;
        setState(STATE_DISABLED);;
        new Email(getEmail();, "User disabled.");;
    }

    Name, State, Email Fields Getters/Setters...
}
0 请登录后投票
   发表时间:2005-09-15  
to:towjzhou
    领域对象User依赖与值对象UserVO,是不是值对象应该比领域对象更加稳定啊?在POEAA中也没有看到类似的写法的
   你的做法有点象"数据保持器"的做法,但作用却不一样,不过partech倒是愿意横切你的这个User(UserVO userVO)方法, 他要是横切User()这个方法难度就大大的了.
    BTW:支持robbin的说法
0 请登录后投票
   发表时间:2005-09-15  
firebody 写道

关于OO的争论,见仁见智。 不同的人根据不同的实践经验会有不同的想法。
正如你说的协同管理必不可少,你可以细心看一下,我提到的两种模型都涉及到了A B C D四种对象的工作,从你所说的对象协同来说,他们没什么区别,然而,他们之间最大区别职责划分上,对于A,这是一种很自然很OO的职责传播机制,从调用层来说,它只知道这个逻辑是属于A的职责,所以它仅仅调用A.logic()即可,而对于图B来说,需要显式的调用A B C D四个对象才能完全一组完整的逻辑,也许你可以在A上定义这么一个方法封装这个逻辑(事实上,很多人都这么做),然而这掩盖不了一个事实,C D对于A可见,意味着A知道太多原本他不应该知道的东西,从根本上来说,对象协作没有发挥其真正的意义,因为A最终很有可能会代理完成大部分的逻辑。 可以就此分析一下:
在A里面写下分别调用B C D等对象的逻辑,很有可能写成这样:

if(b.logic(..) ){

    c.logic(..);
   d.logic();

}
else if(b...){

..
}
虽然形式上这个逻辑由A B C D来协作完成,但其实这个逻辑主体还是由A来完成,而B C D上最终会得到少的可怜的逻辑/职责。 最终可复用的领域逻辑也仅仅体现到了A身上,而且因为A本身包含太多逻辑,所以它本身也失去可复用意义,另外B C D反倒包含太少领域逻辑,失去了封装的意义。
可以提到这么一个结论:原子虽然满足可复用,但过之不足。
我们很多代码都回类似这样,但是它却潜在造就了Action Script思维的滋生。 可以说是比较难以克服的惯性思维。
另外更重要提到一点:
OO永远是思维和解决问题的一种思路,体现在理解和实践上。 在不同场合有其具体适应性 ,就比如 图B来说,在简单的应用中,我往往先习惯用这种类似Action Script的方法来实现,但在复杂的逻辑中,这样的方法很有可能会很不适合,而且coding起来也很不舒服(复杂领域模型会出现这种情况的  )  ,这时候我就会立即废弃类似Action Script的想法(其实一打开始就已经有废弃其的预定想法了。赫赫    )。


管理者存在的主要目的是要成为其所管理的对象体系的代言人,因此管理者必须清楚的知道自己所管理的对象能够胜任何种任务,以及它们之间应该如何协作以完成不同的任务。

而在图a的设计当中,没有任何一个对象知道完整的业务逻辑是如何完成的,当业务逻辑发生变动,需要重新审视如何分配任务的时候,没有任何一个对象能够给出一个高层的抽象视图。最糟糕的是Entity之间产生了不必要的藕合关系,在业务处理流水线当中,Entity实际上关心的只是自己要处理的业务数据,而不关心业务数据是由谁传递过来以及下一步应该传送给谁。

实际上,图a和图b的区别就在于是否将管理职能显式的对象化,至于是否需要管理对象的存在也是视实际情况而定,一般来说,管理对象通常位于较高的抽象层次,较低层次的对象可以自行协作完成任务。所以图a和图b的设计实际上是并存的,不应该以此区分为OO设计或过程设计。
0 请登录后投票
   发表时间:2005-09-15  
age0 写道
firebody 写道

关于OO的争论,见仁见智。 不同的人根据不同的实践经验会有不同的想法。
正如你说的协同管理必不可少,你可以细心看一下,我提到的两种模型都涉及到了A B C D四种对象的工作,从你所说的对象协同来说,他们没什么区别,然而,他们之间最大区别职责划分上,对于A,这是一种很自然很OO的职责传播机制,从调用层来说,它只知道这个逻辑是属于A的职责,所以它仅仅调用A.logic()即可,而对于图B来说,需要显式的调用A B C D四个对象才能完全一组完整的逻辑,也许你可以在A上定义这么一个方法封装这个逻辑(事实上,很多人都这么做),然而这掩盖不了一个事实,C D对于A可见,意味着A知道太多原本他不应该知道的东西,从根本上来说,对象协作没有发挥其真正的意义,因为A最终很有可能会代理完成大部分的逻辑。 可以就此分析一下:
在A里面写下分别调用B C D等对象的逻辑,很有可能写成这样:

if(b.logic(..) ){

    c.logic(..);
   d.logic();

}
else if(b...){

..
}
虽然形式上这个逻辑由A B C D来协作完成,但其实这个逻辑主体还是由A来完成,而B C D上最终会得到少的可怜的逻辑/职责。 最终可复用的领域逻辑也仅仅体现到了A身上,而且因为A本身包含太多逻辑,所以它本身也失去可复用意义,另外B C D反倒包含太少领域逻辑,失去了封装的意义。
可以提到这么一个结论:原子虽然满足可复用,但过之不足。
我们很多代码都回类似这样,但是它却潜在造就了Action Script思维的滋生。 可以说是比较难以克服的惯性思维。
另外更重要提到一点:
OO永远是思维和解决问题的一种思路,体现在理解和实践上。 在不同场合有其具体适应性 ,就比如 图B来说,在简单的应用中,我往往先习惯用这种类似Action Script的方法来实现,但在复杂的逻辑中,这样的方法很有可能会很不适合,而且coding起来也很不舒服(复杂领域模型会出现这种情况的  )  ,这时候我就会立即废弃类似Action Script的想法(其实一打开始就已经有废弃其的预定想法了。赫赫    )。


管理者存在的主要目的是要成为其所管理的对象体系的代言人,因此管理者必须清楚的知道自己所管理的对象能够胜任何种任务,以及它们之间应该如何协作以完成不同的任务。

而在图a的设计当中,没有任何一个对象知道完整的业务逻辑是如何完成的,当业务逻辑发生变动,需要重新审视如何分配任务的时候,没有任何一个对象能够给出一个高层的抽象视图。最糟糕的是Entity之间产生了不必要的藕合关系,在业务处理流水线当中,Entity实际上关心的只是自己要处理的业务数据,而不关心业务数据是由谁传递过来以及下一步应该传送给谁。

实际上,图a和图b的区别就在于是否将管理职能显式的对象化,至于是否需要管理对象的存在也是视实际情况而定,一般来说,管理对象通常位于较高的抽象层次,较低层次的对象可以自行协作完成任务。所以图a和图b的设计实际上是并存的,不应该以此区分为OO设计或过程设计。

具体的东西我不再讨论了,因为我喜欢说抽象的大道理, 
0 请登录后投票
   发表时间:2005-09-16  
<DDD> chapter five - A Model Express in software

"Then there are those aspects of the domain that are more clearly expressed as actions or operations, rather than as objects. Although it is a slight departure from object-oriented modeling tradition, it is often best to express these as SERVICES, rather than forcing responsibility for an operation onto some ENTITY or VALUE OBJECT. A SERVICE is something that is done for a client on request. In the technical layers of the software, there are many SERVICES. They emerge in the domain also, when some activity is modeled that corresponds to something the software must do, but does not correspond with state.
"

Evans总结了模型的四个要素:Association,Entities,Value Object,Services。Value Object怎么用怎么存储我还比较晕。
0 请登录后投票
   发表时间:2005-09-16  
firebody 写道
2)我想重点说一下,约束的逻辑我认为是属于领域模型的一部分,而且他也应该作为模型对象的一个基本特征而存在。 约束的检查可能需要复杂的查找数据的逻辑,这个逻辑的实现不依赖于DAO是不可能达到的。对于3。如果把领域模型分解为实体对象和实体对象管理层,这等于将一个可重用的逻辑分解开来放到两个对象层次中去,而且我也看不到所谓的“领域模型的实现”不能依赖于DAO 的这个结论。 
如果把实体对象管理层分离到具体的业务逻辑中去,而不把它当作领域模型的一部分。 那么我不知道在复杂的领域模型分析中,按照这种思维,得出的最终的领域模型具体能够做到少工作?  具体的业务逻辑我想最终还是移到了所谓的对象管理层中去,这样的领域模型我想可以真正的定义为贫血的领域模型了。


支持,另外谈几点
(1)即使Domain可以直接倚赖于DAO,类似Manager啊,Service只类的东西还是需要的。比如一个UseCase的处理中需要用到DomainObject A和B,两者是非常弱的关联或者就是没有关联,你怎么办?
(2)建议Domain对DAO层的倚赖只限于读数据,这种情况就象你说的,要做判断需要用到些尚在数据库中的数据,如果由Service拿到再交给Entity去判断,逻辑就分到Service和Entity两块了,不便于DomainLogic的重用。 但是写数据的话,建议还是交给Service,Manager去管理,首先是统一,不这样做的话,自己经常可能都会犯迷糊一个写数据的逻辑究竟该Service去负责好呢还是Entity负责;如果允许Entity对象写的话,还要考虑是否可以写自己或者只能写引用对象等;再加上数据库事务被引用到Domain里面,混乱就更多了。
(3)Domain--> IDAO<--DAO , DIP的形式还是可以接受的;Domain-->DAO,最核心的东西核呵,就被DAO绑死了
0 请登录后投票
   发表时间:2005-09-17  
Domain model中完全可以象ejb3那样有一个EntityManager,UnitOfWork,
结合使用Spring时,我已经完美实现: 不是IOC方式.
Service层是通常是由use case 界定的,作为facade是必要的, 关于事务在这层界定比较合适; 业务放在领域模型还是service 中,是有重用性合职责的划分确定的.重用性:
领域模型>service层
0 请登录后投票
   发表时间:2005-09-19  
其实我认为应该是分为两个阶段的:
1)领域建模:  当然与dao没有任何的关系
2)领域模型的实现: 它的实现当然应该是依赖dao的。但不是我们现在的方式,我认为比较通用的方法应该如下:

ThisDomainObject  extends OtherDomainObject
implements DomainObject,XXXDaoSupportInterface
{

}

3)有的人可能会说,上面的XXXDaoSupportInterface应该换成DaoSupportInterface,我认为没有必要。因为你现在是在做实现,必须要选择是 用 木材做房子还是用水泥还是用铁皮。你不能说:喂,我以前是用木材做的,我现在想把它全部换成铁皮,你一天能帮我搞定吗?我不会答应你一天搞定,其实,就算我答应你一天搞定,最后还是要一周才能搞完。

也可能我说的大家会不同意,或者和大家说的不是一个问题?但这个方法的确是我已经在好几个项目中使用了,而且也有 大型维护的过程(相对那个项目来说是大型的)。我并没有发现领域模型依赖数据访问接口有什么问题。
0 请登录后投票
   发表时间:2005-10-12  
一个复杂版本的 active recode 和一个 变异版本的 mapper  应该选择 那个一个呢? 是一个伤脑筋的问题啊。。。
0 请登录后投票
   发表时间:2005-10-12  
当然是mapper了. active record只能应对一些简单的领域模型,复杂起来以后就会往死里搞了。
0 请登录后投票
论坛首页 Java企业应用版

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