实践ddd,太让人沮丧了。。

现在很沮丧啊。。。
前段时间负责的项目,雄赳赳的采用领域对象的方式来建模和编写。
将系统分成: controller > 粗粒度service > context > repository > dao 这几层来进行开发,
同时,借鉴 dci的架构 将bo的行为移到了role中。同时对bo进行了缓存。
本来觉得挺好的,但在实践中,
却遇到了不少问题:
原有系统架构下的缓存在集群下同步问题,
开发方式改变的不适应问题。
除了这些客观的原因。
更致命的是,由于是第一次尝试,经验不足,不能很好的分离领域对象,和它们之间的关系!
很多时候不知不觉就使用了原来的开发方式,
比如,
一个repository,写着写着就成了dao。。。
一个service,写着写着就把role,context的事做了。。
一个bo,写着写着,不知道该咋维护它的状态了,哪里都担心出问题,没有使用db那么踏实。

很多时候不知道怎么用领域设计的思路来抉择,
比如,
记录的分页查询,是直接使用dao呢,还是组装bo?
往前台发送的数据应不应该是bo?
bo太过重量级,它里面的每个关联对象是不是该使用懒加载方式呢,但这样会不会让bo的代码很冗余?
bo的状态该怎么维护呢。。。
等等,这段时间头都大了,不停的修改代码,但系统功能开发并没有多大进展。。
距离代码打包期越来越近,人也越来越焦虑,时时有使用原来的 controller > service > dao开发方式几下子搞定的冲动。。
ddd 远看很美好,实践起来很困难。。。唉, 沮丧的很。。。

2012年02月17日 20:30 "@berserk"的内容
一个repository,写着写着就成了dao。。。
一个service,写着写着就把role,context的事做了。。
一个bo,写着写着,不知道该咋维护它的状态了,哪里都担心出问题,没有使用db那么踏实。 ...

如果说DDD只是指引了一个OO方向,那么当你走上面向对象道路时,还需要更多OO战术知识和技巧来实施。

实施DDD,CQRS读写分离是必须的架构,事件驱动是缺少不了的,我当初开发JiveJdon也曾经碰到过你这样的问题,你可以从JiveJdon 4以前的版本看到这个问题,后来在Jdonframework引入事件驱动后,才更好地将BO也就是领域对象和Service等等隔离开。

这之中经历最大的开发方式转变就是:从过去Service+数据库为主的驱动方式,也就是行为用Service,状态用数据库,这种方式相当普遍,坏处在其他帖子已经谈到了,但是搞了几年这种方式,已经习惯了,改为由领域对象统管封装行为和状态(原来是分散的),就象一群孩子平时野惯了,没有人管,现在多了一个领域对象BO来封装统管,要么管得太死,成了一个大BO死BO,要么管不住,本来属于同一业务逻辑的行为和状态还是分散开来。

当初我用DDD开发JiveJdon时,也曾经遇到这个问题,属于孤独无援,还好是开源,大不了废了,在经历几乎失败之后,你才能发现柳暗花明,自身修行上了一个新阶段。


[该贴被banq于2012-02-18 10:12修改过]

最近一个项目也想尝试DDD,最后种种原因用回了Bo+dao的模式。个人想自己弄弄实践下,再在公司中使用。
Lz,还是慢慢消化下,至少会慢慢理解DDD的,只要事情做了,总比没有做好。

在实际过程中,我有几个问题一直没想得好。
第一:ddd的最大好处是提炼了bo和相关的行为,使系统代码设计不是以前台命令驱动,而是还原领域中的各种概念。
这样就有一个问题了,如果,我对某个重要bo的理解有误了,并且直到系统开发接近尾声时才发现,不得不进行修改,由于系统不是使用原来的简单层次结构开发模式,因此,充血的bo与其他场景,bo实质的产生了不少行为交互,实质上是代码产生了多处耦合的情况。这样,当我修改这个bo时,很容易牵一发,动全身。
在实践里,我就经常遇到这个问题,对oo抽象不到位,导致修改bo,与之关联的很多代码都需要修改了。。。而不是原来的直接写个service解决一切,出问题也只修改部分地方,虽然这个几部分的代码没多少可读性。。

第二:bo必须存入缓存,ddd才能有效率可言吗?开发中,bo的创建代价很大,因为它有很多关联对象需要取查询。同时,bo的状态随时在变,如果随时都去连db,效率可想而知。
按照原来贫血模型的方式,查询一个实体的代价是不大的,因为实体只相当于一个vo,没有很多关联。因此,我可以不去怎么关注实体是否需要缓存起来,(系统大了之后,db holde不住,这个问题也逐渐暴露了。。)
在实践过程中,显示列表这种操作总让我心惊胆颤,因为,这意味着让我得去查询一堆bo。。。
cqrs的架构,我不是很了解,但它能解决我这个问题吗?

第三:实践中的协作开发,不知道该如何划分职责,ddd能提高协作开发的效率吗?
按照原来以前台命令驱动的设计方式,很大程度上让各自的开发过程隔离了,虽然每个人可能重复实现了某个公共业务,可能整个系统代码设计能体现出来的业务逻辑比较支离破碎。但由于隔离了开发过程,多人能够并行开发,因此它确实提高了开发效率。
但用ddd的方式,某个bo可能会横跨整个系统,某个业务需要多个bo协同完成。。
但实际上,开发人员不可能同时都写好这些bo,总是遇到,这个等那个,那个等这个的问题。 在我实践ddd的期间,调整了多次开发分工:按模块分工、按接口分工、前、后台页面分离开发。。。但效果不是很好。。。我很想知道,使用ddd的情况下,多人协同开发时,该如何分工。。。或是我该去看看xp?
上面这几个问题,望解答。。

强烈关注,个人目前还没有完整经历过DDD驱动开发,个人只是在业务分析整理过程中会使用这种思想去抽象去总结,实际的开发过程并没有采用这种模式,仍然是采用脚本模式来做的。

2012年02月20日 18:50 "@berserk"的内容
实践中的协作开发,不知道该如何划分职责,ddd能提高协作开发的效率吗? ...

前面两个问题可以通过引入DCI 和CQRS解决,我就不多说了,关于开发方式方面,DDD属于一种MDD模型驱动开发,何谓模型驱动,也就是什么都没有的情况下,首先从需求中分析出领域模型,然后不断的迭代丰富完善它。

你原来按照Service功能来划分,好像需求也是按功能用例来表示,那么是不是接下来步骤都是按照功能来分别设计开发呢?肯定不是,不能因为目标是功能,你的实现途径也是按照功能,这样就会忽视领域中的模型。打个比喻:老子思想的无为是一种目标,那么你要达到无为而治的目标,实现途径反而需要反者道之动,必须有为才能实现无为目标,无为而无不为。

过去数据库驱动开发,也就是首先从需求中找出实体表;而MDD模型驱动开发则是从需求中直接找出领域模型的实体,两者有些类似,都是静态的,但是有本质区别。

所以,建议你不要用BO这个名词来指称领域模型,因为BO是业务模型,是Service驱动开发下对领域模型的“蔑称”,Domain Model领域模型才能体现业务高于架构(包括Service)的概念,也才能让大家知道什么是大家的统一语言,不是服务,不是功能,而是Domain Model。

所谓统一语言,就是大家用统一的指称语言来表达业务,单用一个BO太笼统了,太笼统反而是不重视,Domain Model包括实体 值对象,其中的区别的学问大得很,你可以参考Jdon这方面几年的讨论。

全新改变开发方式,从重功能设计转变到重领域模型提炼和精炼上来,这样你的BO才不会是一个庞大的类,好像一个庞大的Service类一样。对BO进行分解,从名词开始分解,不用BO笼统的,而是直接指称哪个是实体,哪个是值对象 。



[该贴被banq于2012-02-21 18:16修改过]

楼主刚学DDD,这些问题是正常的。

DDD,领域建模是核心。

以DDD为开发模式的设计开发步骤可以是:
1)分析需求;
2)画出用例图,系统中各个角色如何使用系统,也包括外部系统如何使用系统,也包括系统中到某个时间点自动启动的某些功能(此时角色就是时间);
3)针对各个用例图,就知道了系统使用的各种业务场景,同时也明确了系统的边界,从而就明确了领域模型的边界;
4)在领域模型的边界内划分聚合,找出每个聚合的边界,找出边界内的聚合根,实体,值对象;这步是难点。这里一定不能混淆的一个概念是,领域建模不是以用户为中心的建模,而是以用户的需求为中心的建模。所以要努力寻找各种业务概念,切勿将所有行为都设计到User,Employee,Account,等对象上。而应该找出如Order,LeaveRecord,Payment,JobApplication,等业务概念。
5)如果是根据经典的DDD领域建模,我们可以接下来分析一些领域服务,领域服务用于协调多个领域对象之间的行为;
6)根据领域模型中的聚合根设计对应的仓储;这步完成就表示领域模型已经完成了。
7)对照前面的需求用例,检查领域模型是否都可以满足用例中提到的各种业务场景;
8)进一步分析领域模型,分析模型中哪些点是和特定系统相关的设计,哪些点可以进一步抽象出通用的领域模型,该通用领域模型可以满足此类相似系统的核心业务。比如积分系统中,可以抽象出“积分发放/消费”的模型。该模型可以在任何财务或积分系统中使用;
9)现在,可以让有经验的人检查一下你设计的领域模型了,也就是如果有可能就进行一下模型Review,确保在写代码之前,模型的正确性。
10)对领域模型进行单元测试,单元测试的时候,如果只想测试业务逻辑,可以设计Stub的仓储;如果想和持久化一起测试,那可以编写真实的仓储,如果你是用Hibernate来做数据存储,可以在此时建立ORM映射文件和数据库表;写完后,编写测试用例进行单元测试;
11)最后实现你的UI层。但是为了让你的UI层可以不依赖于你的模型设计和测试,可以在UI层编写他自己需要的ViewModel,然后他在Web Form或Controller或View上只要访问ViewModel即可。等到你的领域模型完成并测试通过后,把领域模型的数据填充到ViewModel即可;

差不多就是这个步骤吧,仅供参考。
[该贴被tangxuehua于2012-02-22 12:55修改过]

2012年02月22日 12:52 "@tangxuehua"的内容
差不多就是这个步骤吧,仅供参考 ...

写的很好,通俗易懂。

刚刚贴了一篇Scala实现领域模型的代码,大概是在楼上步骤的第10步以后开始实现的。

[该贴被banq于2012-02-22 13:50修改过]

2012年02月22日 12:52 "@tangxuehua"的内容
所以要努力寻找各种业务概念,切勿将所有行为都设计到User,Employee,Account,等对象上。而应该找出如Order,LeaveRecord,Payment,JobApplication,等业务概念。 ...

这里不是太明白,Order,Payment应该表示“下订单”,“支付”的概念吧,这两个概念是动词,为啥也会弄成类呢?

这些是动词的结果,下了订单后产生Order对象,应聘了某个职位后产生JobApplication,付了某笔钱后产生一个Payment。

2012年02月27日 21:51 "@tangxuehua"的内容
动词的结果 ...

好的thx。我记得《pojo in action》里,好像就是这么分的,当时我怎么都理解不了。另外,除了《pojo in action》,还有别的讲ddd在java领域的实践的书吗? 尤其是在 ssh/j2ee结构下的。。。 jdonframework 有些东西太超前了。。。我理解不了=。=!
[该贴被lostalien于2012-02-28 12:01修改过]

我是搞.net的,对java平台不太了解。

请教, DDD里面讲了Factory,这个Factory具体指什么,还有以Cargo为例子讲了一个Handling Event Entity,这个Handling Event起什么作用?

我的感觉是DDD这种东西在分析阶段是还很好用的,在设计编码阶段太别扭。
领域模型的价值在于提供一种通用的语言,使得领域专家和软件技术人员联系在一起,沟通无歧义。
按照霍顿(F.W.Horton)的信息资源管理(IRM)理论,数据才是企业最重要的资产,企业总体数据模型是需要早早就规划出来的。
用DDD的方式提炼核心模型(静态模型),用DCI的角色表示逻辑和行为(动态模型),更便于同领域专家沟通。
在企业信息化开发的过程中,在新做一个项目时企业总体数据模型经常是早就定好了,只是根据新的业务实现其功能。
业务规则是经常变化的,试图设计一个不变的领域模型(动态模型)是不现实的。

说领域驱动设计只适合大型项目,不适合小型项目,是这样吗?