把CRUD封装如service中,如果DOMAIN类的业务方法要使用CRUD,那就把这个Service注入到DOMAIN中?

InfoQ这个案例确定让人感觉很奇怪。

Load.add(newLoad). Load 本身增加一下新的 Load

这个理解起来不好理解, 这个 Load.add(newLoad)能不能理解 为 Load + newLoad 的呢?
如果是 Service.add(newLoad) ,那就肯定是新增加一个newLoad对象了。

我也研究过 InfoQ这个案例,感觉这个 Model 根本没有表达该案例所描述的需求。按DDD的说法,模型应该是能丰富的展现该领域的业务意义, 但这个模型只是一个简单无业务意义的 CURD , 没展现出 Load,FundingRequest,Borrower之间的业务意思. 可能由于是案例,作者简化了模型,但是案例对于说明 DDD 的实际意义不大,估计是为了展现 DTO 封装吧。


对于《领域驱动设计和开发实战》,说的实战经验,对于我们这些新手来说,很值得学习。
[该贴被BinnyJ于2009-02-09 11:31修改过]

其实领域模型设计归根到底就是“类的职责”问题。

一个订单的保存,修改,删除,可以认为是“订单”的职责,所以这些crud放入到“订单”中就很正常。

生成报表的时候,会需要订单、员工、业绩等等许多领域对象,但是系统中没有“报表”这么一个领域对象,所以放入到 service中会比较合适。
(当然,如果经过分析,系统中存在“报表”这样的领域对象,那生成报表的操作就放入到“报表”中)

>一个订单的保存,修改,删除,可以认为是“订单”的职责

这是一个很有趣的讨论:一个反方观点是:一个订单的保存,修改,删除应该是订单管理者的职责,而不是订单本身职责。

从语义上看:保存修改删除好像属于订单的管理工作,而不是订单自身内在自发的动作。

不知大家如何看这个分歧?

"
这是一个很有趣的讨论:一个反方观点是:一个订单的保存,修改,删除应该是订单管理者的职责,而不是订单本身职责。

"

这其实不难理解,如果存在“管理者”这个模型,而订单的保存与这个管理者密切相关,那么我们完全可以使用 管理者.saveOrders() 这样的方式来保存订单。

但如果管理者不是一个领域模型,而仅仅是一个 role ,那么使用 order.saveOrder 就好了。

我在另一篇帖子里也发表了一下我的看法,我认为可以把这看成被动调用,像监听器一样。每当我要被保存的时候,我就通知一下管理者,叫它反过来保存我。由于现在我不用特别去理解管理者,它对我的应用来说是存在但不被重视的,于是就把它放进Model背后,而这时的Model类充当领域实体和Dao的结合,因为我从不关心Dao,而它在背后实现,于是在视觉代码上体现出来的就只有领域实体的特性。
而Java在这方面做得和ror等就不一样,它没办法这么做,要做的话,在视觉上就有显式的dao,于是一旦看到dao,我就知道原来它在那里。当然也可能会利用特别的编译器在编译期生成dao,从而在视觉代码上看不到dao,但实际在编译后还是有dao,但那都不需要人为管理了。
[该贴被freebox于2009-02-09 13:41修改过]

嗯,可能就存在这样的管理者,或者说 生产制造者,就象工厂和产品关系一样,工厂是产品的生产制造者,也就是管理者。

DDD中认为repository就是这样管理者,所以就有orderRepository.saveOrders() ,infoQ这个案例也是这样,引起争议的是,它又让Order来引用oderRepository,相当于又绕回来。

也就是说:client客户端是直接调用orderRepository.saveOrders()来保存删除修改订单,还是client客户端调用order空订单对象,然后order.orderRepository.saveOrders()?这不相当于order.saveOrders()吗?

order可能是空订单对象,但不是空订单+管理者对象,我认为代码上的order和领域实体上的order是有区别的,但ror等当中,order更多体现了领域实体的特性,背后的dao机制不在视觉当中。

>Java在这方面做得和ror等就不一样,它没办法这么做,要做的话,在视觉上就有显式的dao,于是一旦看到dao,我就知道原来它在那里。当然也可能会利用特别的编译器在编译期生成dao,从而在视觉代码上看不到dao,但实际在编译后还是有dao,但那都不需要人为管理了

是这样,所以,我之前也呼唤能不能有一个java持久层框架能够定时或隐形地自动进行实体持久化。就象我前面说的,兵马俑这个分布式缓存现在可以做到这点,这样也许我们不需要再显式保存。

但是,上面只是技术手段一个模糊而已,实际上没有从逻辑上回答我前面提到的两个观点,而且如果谈DDD的话,repository是独立于产品订单的管理者好像更有说服力一些,这也是SOA一个基本逻辑点哦。

看来,SOA和DDD思想并不矛盾,而是RoR这种处理方式让人疑惑,出于代码实战方便,可在OO理论和逻辑上有待解释,不知是否这样?

这些其实也并不矛盾,因为DDD不是目的只是一种实践。

记得一句话,好像是M$的比大叔说的吧,“没有绝对的安全,只有足够的安全,看你有什么样的需要了。”

在我们现实的软件实践中经常会出现在“足够”的前提下出于简化的目的而简化程序的结构和实现,完美是需要代价的。

我觉得RoR很主要的一个目标就是尽量高效的面向业务过程进行开发,因此其他的一些诸如:扩展性、完整性、理论性,等必然会与“方便快捷”产生交换,现在的结果也是作者找到的一个平衡点。我们是在进行生产,成本是一个重要的元素。

这和ROR的方式是一样的。本质就是将仓储和实体在形式上合二为一,在进行CRUD操作时,它就是仓储。
比如order.saveOrders(),就应该理解为orderRepository.saveOrders(),这时的order是orderRepository的替身,因此代码实现上,order的CRUD就是对orderRepository的代理。
在ROR中,仓储的一些标准操作如CRUD等,是由ROR直接支持的。java中,其实也能实现类似的效果,只不过不如动态语言来得灵活。

单纯从save这个动作来看,可以看成由order自己完成,它自己给自己在持久层留一个备份,但是CRUD中的创建create这个动作放在order中就不太符合OO逻辑了,面向对象告诉我们,一个对象只有创建它才能使用它,因为order还没有生产出来,是一个空的,怎么能使用它?从常识讲,万物皆有母,任何事物不可能自己产生自己(当然宇宙除外)。

关于Repository,DDD定义:用一个全局接口设立访问入口,提供增删模型对象的方法,把对数据存储的实际插入和删除封装起来。让客户聚焦于模型,把所有对象存储和访问工作委托给仓储完成。

注意,定义中仓储是全局的,是访问入口,也就是说,是客户端首先访问仓储,通过仓储获得模型对象。所以,仓储应该看成是一个容器或平台基础功能,在ROR中仓储是动态语言这个基础平台自己完成的,而在Java中,我们就不能生搬硬套RoR做法,将仓储嵌入模型对象,因为Java中本身就有明显的框架基础平台(也就是容器概念)和业务模型之分。


我觉得可以这样理解,如果order是空的,那调用save的时候为什么没报告给我空指针呢?这说明它本质上不是调用业务order来保存自己的,只是在调用一个背后的像父类方法似的东西来处理这件事。order也不是业务上的order,它包含了背后的dao但dao对程序员来说不可编程,是编译器管理的,所以就只体现了它的业务特性。
像java里也有字节码增强,加了好多东西进去但对程序员来说是不可编程的,没看到不等于不存在,它在那里但却不需要对它编程,让它体现更多的可编程的业务性质,把其它的逻辑交给编译器、增强器处理。
所以IoC/DI是技术手段,不是DDD,技术可以不断发展,业务从来都不会跟随技术的发展而改变实质,业务的扩展要遵循它自己的规则,所以几年之前没听说有啥EJB、Spring,业务还是照样运行,等几年之后EJB、Spring之类的都完蛋了,业务还是业务自己。如果把这种业务对象里加进了技术性质并且还让它是可编程的,就不那么好办了,模型就需要重新考量(像我们总想在实体里DI进某个领域服务,这不是不行但有技术成分,而以后可能是定义规则直接发现服务而不需要DI,没有技术成分的模型更好),如果技术性质被委托给了编译器增强器处理,模型就是模型,只要有新的编译增强器,就能让它重新适应新的技术。

同意banq的说法.但crud放在domain model中确实能减少大量的service类.
另外我认为如果业务没有明确的service. crud也可以放到service facade中去,粒度更粗一点,当然同样是通过 repository
[该贴被mygol于2009-02-14 09:05修改过]