论坛首页 Java企业应用论坛

再次小结领域模型的种种观点

浏览 146174 次
该帖已经被评为精华帖
作者 正文
   发表时间:2005-12-16  
wolfsquare 写道
如果要钻牛角尖到底的话,小额活期收年费这种逻辑不应该是帐户的业务而是银行的业务,所有该方法是属于"银行"对象的,xixi~


这个牛角尖钻得非常棒!但是是否合理,还需要深入思考和实践。实际上,通常意义上凡是account的无状态操作(或者说被动操作)应该统统都是bank对象的业务逻辑方法,例如:

bank.addAccount(account);
bank.updateAccount(account);
bank.getAccountAverageBalance();
bank.getAccountAnnualFee();

但是如果引入bank这个对象,那么bank承担的职责会实在太多,他会有n种职责,账户管理,报表管理,票据业务,信用卡管理,。。。。。,那么这个bank也太大了一点,根据OO的单一职责的原则,我们可以把bank的每种单一职责分离出来,bank的账户管理职责分离到accountManager里面,bank的信用卡管理分离到creditCardManager里面,。。。。。

于是Account和AccountManager的两元结构在OO理论的指导下又被重构出来了~
0 请登录后投票
   发表时间:2005-12-16  
robbin 写道
firebody 写道
引用
转账操作 transfer, 有疑义了,转账不依赖于某个特定的account,是一个抽象的通用操作,简单来说,它是一个无状态的操作,如果是你,你可能认为转账是一个银行账户的紧密业务逻辑,也确实如此,所以 account.transfer(account1 ,account2)。

no  no ..

一般这样来 account.transfer(destAccount)  .

一开始就强行假设,后面的所有论据都有点站不住脚。

AccountManager有存在的必要,正如你所说的。 事务确实是非常谨慎考虑的事情。然而存在AccountManager之后,会跟Account引起很多仍然可以争议的地方,这里是仁者见仁,智者见智。

另外对于你提到的一个操作逻辑,不知道该把它放到哪个对象身上,这点我想是一个经验和设计问题,几乎所有的操作肯定是基于某个对象促发的,因为一切都是对象。就所没有任何得***Service ,***Manager,全部是Domain model ,你仍然可以很OO的将Operation ,message定义到某个领域对象上,只要你对它提出需求,你就可以设计。


好吧,就不说transfer,比如说我想知道所有账户的存款平均余额:
getAverageBalance(),这业务方法他和Account领域模型紧密关联吧,那么请问
account.getAverageBalance()合适吗?
你account对象没有理由知道我银行所有账户的存款余额呀?

这样的情况
Accounts.getAverageBalance() 比 AcountManager.getAverageBalance() 更好些,
AccountManager更像是关于Account这个modul的业务脚本,
而Accounts是建立在这些业务脚本上更高一层次的OO抽象,
两个干的事差不多,但是概念不同,
当工具足够强大后,我们的domain modul就无须为事务的特点所妥协(会自动合并,设置事务边界),
事务应该散布在整个domain modul里。
0 请登录后投票
   发表时间:2005-12-16  
robbin 写道
Hi,等不及firebody回复了。我先总结一下我的结论。

transfer这个例子举的不好,getAverageBalance也让firebody钻了空子,getTotalAnnualFee不知道firebody还有什么花样。但是我想表达的观点是:

充血模型号称要把操作和数据放在一起。认为把操作和数据分离开来的做法是不OO的。实际上这个结论非常空泛,并不是任何情况下都应该把操作和数据统统放在一起的。我前面举那么多例子就是证明这件事情。

这个问题需要分两个阶段来考虑:

一、系统分析阶段
在这个阶段,毫无疑问,只有一个Account抽象领域模型。

二、编码阶段
在这个阶段,一个抽象的Account领域模型并不是只映射到一个Java Class上面去的,即使抛开DAO接口分离持久化细节的考虑之外,也不应该只得到一个Account Class。
因为针对Account抽象领域模型来说,有两类操作:一类是有状态的操作;另一类是无状态操作,这两类操作从业务逻辑角度来说,都应该建模在Account抽象领域模型上,但是这两类操作到了编码阶段,就不应该统统塞进一个Class里面去。因为无状态的操作不是由具体的account对象触发的,也就是partech所谓的“被动”含义,应该是一个static的方法,但是IoC无法对static进行DI。这里受到了Java语法的限制。

如果没有IoC,那么我同意Account领域模型只映射一个Account Class。无状态操作定义为static的。但是IoC的情况下,我们必须进行职责分离,把无状态的操作分离到另外一个Class里面去,例如AccountManager,这个AccountManager可以是Singleton的。

在编码阶段Account Class和AccountManager Class不能孤立的看待,他们是互相协作的两个Class,共同代表了Account抽象领域模型。

因此结构是这样的:

web action ---> domain object(account , accountManager) <---> DAO

事务控制放在account和accountmanager上面,所不同的是account是prototype的,有状态的,accountManager是singleton的,无状态的。

此外DAO是不应该被暴露给web层的,DAO是持久化实现代码,它不负责事务控制,它没有这个职责。同时请注意,所有的DAO接口方法统统都是无状态的,包括update,merge,persist操作。另外,很多DAO方法本身也是业务逻辑,就像getAverageBalance,他可以用一个DAO方法去实现,但是,getAverageBalance也有确切的业务含义,应该是一个无状态的,建模在Account抽象领域模型里面的domain logic。因此,accountManager也必须包含这个方法。简单来说,就是AccountManager应该封装DAO接口。

这样的充血模型才是合理的领域模型。在Spring2.0时代,我到是愿意尝试这一这种架构。而我觉得你提出的架构很混乱,不合理的地方也很多,经不起推敲。

这是一个很正统的思路,我赞同这样的思路。但是我不赞同这样思路下的某些产物。
0 请登录后投票
   发表时间:2005-12-16  
这个世界不只有object,还有behavior,commucation,就像人和人之间的交往不只是两个个体这么简单,还有他们的关系,社会环境等等,所以Service层是必须的,描述对象交互的过程。

当然如果Service不对外发布,比如提供给第三方,又是简单应用,就放在Action里面也未尝不可。
0 请登录后投票
   发表时间:2005-12-16  
robbin 写道
wolfsquare 写道
如果要钻牛角尖到底的话,小额活期收年费这种逻辑不应该是帐户的业务而是银行的业务,所有该方法是属于"银行"对象的,xixi~


这个牛角尖钻得非常棒!但是是否合理,还需要深入思考和实践。实际上,通常意义上凡是account的无状态操作(或者说被动操作)应该统统都是bank对象的业务逻辑方法,例如:

bank.addAccount(account);
bank.updateAccount(account);
bank.getAccountAverageBalance();
bank.getAccountAnnualFee();

但是如果引入bank这个对象,那么bank承担的职责会实在太多,他会有n种职责,账户管理,报表管理,票据业务,信用卡管理,。。。。。,那么这个bank也太大了一点,根据OO的单一职责的原则,我们可以把bank的每种单一职责分离出来,bank的账户管理职责分离到accountManager里面,bank的信用卡管理分离到creditCardManager里面,。。。。。

于是Account和AccountManager的两元结构在OO理论的指导下又被重构出来了~

Domain Model(1)-- Domain Model != 真实世界- -
http://partech.blogdriver.com/partech/188881.html
0 请登录后投票
   发表时间:2005-12-16  
robbin 写道
因为针对Account抽象领域模型来说,有两类操作:一类是有状态的操作;另一类是无状态操作

为啥采用这样的分类方法?有啥依据和好处?
0 请登录后投票
   发表时间:2005-12-16  
引用
这样的情况
Accounts.getAverageBalance() 比 AcountManager.getAverageBalance() 更好些,
AccountManager更像是关于Account这个modul的业务脚本,
而Accounts是建立在这些业务脚本上更高一层次的OO抽象,
两个干的事差不多,但是概念不同,
当工具足够强大后,我们的domain modul就无须为事务的特点所妥协(会自动合并,设置事务边界),
事务应该散布在整个domain modul里。


经过wolfsquare的启发,我明白account的这些无状态的被动操作其实都是bank领域对象的主动业务逻辑方法。如果外沿无限扩大的话,我们总可以消除所有的无状态被动操作,让系统只剩下单一的领域对象和DAO接口。

叫Accounts确实比AccountManager来得舒服那么一点点,但是概念没有什么不同,都是用来分离bank的职责的。

现在spring的声明式事务已经强大到自动合并,设置事务边界了,事务确实可以散落在domain model里面,但是就算这样,你还是少不了Accounts。
0 请登录后投票
   发表时间:2005-12-16  
很多时候,我看到的Manager对象的代码都是一个interface ,一个IMpl
然后哪个ServiceImpl(ManagerImpl) 几乎每一个方法都是delegate到对应的dao或者某个被操作的实体上。

哪些复杂一些逻辑的方法姑且不说,这些方法我怀疑有其存在的必要性。 写在Service绝大多数的原因都是获得事务。 然而现在的事务管理已经做得非常好,把事务放在Action中,如果能够去掉这些代码,我觉得是值得考虑的。当然,可能者需要一个比较谨慎的设计。

另外说说Account ,AccountManager 。我想我原来较为混乱的想法已经逐渐有一个清晰的概念了。
是否将操作定义到Account身上又一个标准。
那就是这个操作所操作的数据是否都属于这个实体的属性,是否需要一个比较复杂的Query。

如果是前者,那么毫无疑问这个操作是定义到 这个对象身上的,对于后者把操作
定义到Manager更合理一些。
然而这个会带来一些问题,因为领域模型设计的不合理,一些操作本着理解来说应该定义到这个实体上,然而因为关联的不合理设计,导致需要query才能够达到。 这就导致了一个争论的导火索。
0 请登录后投票
   发表时间:2005-12-16  
firebody 写道
很多时候,我看到的Manager对象的代码都是一个interface ,一个IMpl
然后哪个ServiceImpl(ManagerImpl) 几乎每一个方法都是delegate到对应的dao或者某个被操作的实体上。

你这里所说的manager,是属于Service Layer吗?还是Service Layer下面的
Domain Layer?

感觉你似乎没有找到ServiceLayer存在的理由。
0 请登录后投票
   发表时间:2005-12-16  
robbin 写道
引用
这样的情况
Accounts.getAverageBalance() 比 AcountManager.getAverageBalance() 更好些,
AccountManager更像是关于Account这个modul的业务脚本,
而Accounts是建立在这些业务脚本上更高一层次的OO抽象,
两个干的事差不多,但是概念不同,
当工具足够强大后,我们的domain modul就无须为事务的特点所妥协(会自动合并,设置事务边界),
事务应该散布在整个domain modul里。


经过wolfsquare的启发,我明白account的这些无状态的被动操作其实都是bank领域对象的主动业务逻辑方法。如果外沿无限扩大的话,我们总可以消除所有的无状态被动操作,让系统只剩下单一的领域对象和DAO接口。

叫Accounts确实比AccountManager来得舒服那么一点点,但是概念没有什么不同,都是用来分离bank的职责的。

现在spring的声明式事务已经强大到自动合并,设置事务边界了,事务确实可以散落在domain model里面,但是就算这样,你还是少不了Accounts。

我们应该可以看到我们的工具正在朝这样的方面努力,所以我们也需要更开阔得看这些问题,我们的domain modul也可以应该也能够丰富到很丰富,我觉得我们今天讨论还是得到原先的结论,是有些保守了。
Accounts是必须的,因为他本身就是这个modul里的一部分,在整个modul里每个modul都各司其职,但是不应该以事务来再区分modul里的这部分和那部分(xxx and xxxManager)。这是我们原来的一种妥协,我们现在就是置疑这种妥协的必要性。
0 请登录后投票
论坛首页 Java企业应用版

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