DDD引入的几个问题

我在我们的项目中引入了ddd的分析方式,可能有点粗略,但是我尽量往ddd靠近,有几点迷惑:
1.统一语言:我们掌握需求信息,根据获取的用户需求列表来分析业务;根据我们的经验来抽取统一语言;我们使用统一语言来建模,实现对象之间的关联关系,但是最大问题是,我们确定的这个统一语言是在变化的,尤其是跟遗留系统产生冲突的时候,很多人不适应这种方式。我认为这种分析层次上的重构是相当必要的,但是会受到参与人员的业务和技术水平的影响,我们的参与人员中没有业务代表,只有熟知业务的开发人员,这个是我一直非常担心的。在这种情况下,各位有没有好的建议,如何降低这种需求片面性风险?
2.实体与值对象的区分:值对象,应该是比较好分割,我们只要把握住一点:免疫的,携带信息的无责任对象。那么对应的实体,就让我们现在比较迷惑,到底什么样的对象就应该做实体,到底什么样的实体应该做聚合根?现在的实体,从持久化层这个角度看,都是一个简单的POJO,尽管我们可能把某个对象置为实体,但是它就是作为一个可以持久化的POJO,对于我们关心的实体的特征:有标识的,有生命周期的业务对象。这个概念使得我们更加迷惑,还是不知道如何区分。希望各位高手给点建议。
3.Service的角色问题:
Service到底是用来做什么用的,根据Eric的观点,这个service应该是独立于实体,也就是那些不依附于任何对象(在此应该指实体对象)的方法的集合。既然我们的Entity都变成了POJO了,那么我们这个service就很容易把我们以前的思想(mvc结构中的action-service-dao分层模式)给抽出来了。所以,这个service又该如何界限呢?
4.所幸,我们还可以区分Factory和Repository,不多说了,但是鉴于上面的3个问题,可能这一部分也是存在问题的。

望各位兄台能给些许意见和建议。。。
小弟在此感激不尽!!!!!

个人观点:关键是只是靠DDD是远远不够的。

要实现统一语言,必须有统一的设计分析模式基础,也就是是模式大学毕业的,如果不深刻理解结构型 行为模式 等,很难达成统一语言。

关于Service定位,也是因为只有DDD是不够的,DDD只是侧重分析结构,没有强调行为动作,建议参考这篇文章:

如何从职责和协作中发现丰富对象?

在分析的过程中,加入设计模式是必然的,我们也有引入的
现在就是在分析层面,我们还是会出现这种混淆的问题
感觉ddd的基础分析和重构也有些混淆的原因在里面

2010年04月20日 13:00 "taochenpfj"的内容
我们还是会出现这种混淆的问题 ...

分析的结果最后体现在三张上图:类图(代表结构 主要解决是什么,隐藏细节, 由子系统 模块 包 对象群实体和值对象组成); 协作图(类图中对象是在具体场景功能中如何定位,角色是什么,角色之间如何协作?);顺序图(代表行为协作细节,协作图中具体协作顺序 细节是什么?)。

类图是解决结构问题,协作图和顺序图是解决角色 职责协作问题。DDD只侧重前者类图,你看这本书通篇很少见到顺序图或活动图或协作图,归根到底还是一本以数据结构分析为主的建模方法。不全面的。

只靠DDD是得不到这三张图的,只能得到一张类图,只有类图是不能干活的,因为只说明了“它是什么”,没说明,它“干了些什么”,是“怎么干的”。程序员拿到你的类图还是没法干活。

关于实体的疑惑,也是出于此,这也是DDD叙说不足的,实体中一定是包含可变的重要状态,实体一定有重要的职责,一群对象在一起工作,支持一个大的职责,根实体就代表这个大职责代表。

有些设计如果只是从静态特征去分辨,不能确定的,如果我们从动态职责再去分辨,两条路相辅相成,那么就基本能得到一个肯定结果,所以,只靠DDD中的特征辨识是不够的,职责是一种行为特征,应该特别的加以重视。

不是有那么一句:
面向过程程序=算法+数据
面向对象程序=对象+消息

DDD只解决了“对象”的问题,没有解决他们之间是如何通过消息进行协作的。所以,只是提炼出实体对象是不够,实体的重点不只是状态数据,还有导致这些状态数据变化的行为,这些行为我们要去建模,达成统一语言。


[该贴被banq于2010-04-20 14:05修改过]

是的,是的
我看了Eric的ddd后就在考虑,难道uml要退役了???
看了四色模型之后就在想这种对象间的关联关系
现在跟组内在做领域分析的时候,就是直接在纸上建模,直接画出对象间的关系,但是真正的设计关系还是没有uml里面的类图明确,对象之间的调用关系也没有用协作图来实现
听君一些言胜读十年书啊
自己可能有点死读书了,不过其实我还没有完全读完Eric的书,后面关于分析模式方面的一些东西也是比较深奥

2010年04月20日 14:07 "taochenpfj"的内容
Eric的DDD后就在考虑,难道uml要退役了??? ...

个人以为,DDD中提倡的统一建模语言,完全可以UML这个统一语言为平台,大家在同一个语言基础上交流沟通,当然,对于简单案例,也可以是CRC或Story等统一表达方式,不过UML要跟全面更专业一点。

DDD只是强调了方向,至于如何到达方向并没有细说,这就要结合其他已有的OO分析设计经验来丰富完善。

现在其实就是分出了两个方面的东西:
1.业务领域的建模
2.代码实现
ddd追求的是两者的统一,无论是用uml实现还是直接在纸上实现都是对象之间的关系
而ddd中很重要的一点就是如何划分实体、值对象、service,其他部分如factory、repository等方面我们还是比较好区分,因为这些部分要么是为了封装聚合根的创建复杂性,要么是跟底层的缓存、持久层耦合实现的

我们现在的实体、值对象、service太模糊了,这是我们最头大的地方。

是不是值得跟踪的那些对象就应该是实体,那么这些实体的存在周期,又该如何在系统中确认呢?
我现在对这个生命周期比较纠结,我们就是从数据库获取对象,然后提交到domain里面去处理(这里就比较郁闷,扔给facade或service去处理了),到底怎么做就是判别这个对象消逝了,怎样就算是存在了?

2010年04月20日 14:48 "taochenpfj"的内容
,到底怎么做就是判别这个对象消逝了,怎样就算是存在了? ...

我的做法是把这个对象从Repository创建出来后,就放在内存中,其实是放在缓存中。

因为这个“取放入内存”这个行为是可重复执行的,只要涉及到使用这个实体,我就先通过"取放入内存"这个行为来获得这个实体对象。

也就是说实体对象是即用即取的,实体对象的生命周期应该假设是Application级别的,可被认为是常驻内存的,不知你为什么要判断实体的生命周期

鄙人以为,统一语言是需要和具体使用用户一起建立的。DDD强调抓住最核心的内容,让其在用户和开发人员心里有个统一的模型(毕竟需求变化大多来自使用的用户。。。)。太拘泥UML和建模工具往往会分散了注意力。所以DDD一书都没有用很正规的工具就画,用笔和纸往往更让容易表达。巧合的RDD的方式CRC卡片也是如出一辙,简单卡片让人迅速的抓住重点了,而不会饶到细节去了((⊙o⊙) 贴满整个房间的小卡片是多么恐怖的事情~)。

2010年04月20日 14:48 "taochenpfj"的内容
是不是值得跟踪的那些对象就应该是实体,那么这些实体的存在周期,又该如何在系统中确认呢? ...

要分清哪些对象是实体或值对象,我觉得有两步可以参考:
1、从需求、业务分析入手,有业务ID(非数据库ID)的对象是实体,否则先暂时定为值对象
2、从类-类之间的关系入手,如果有一个类的对象是要依赖其父对象的存在而存在的,那么可暂时将其定为值对象。
3、值对象的信息一般在其生命周期内是不可变的

另外,实体或值对象的确定是一个随着分析和设计不断迭代而逐步清晰的过程,并且是根业务需求/业务场景密切相关的。比如客户、地址这两个类,在不同的系统中会有不同情况,举两个很粗的例子:
1、在一个电子商务系统中,有可能客户是实体,而地址是值对象。因为从分析得出地址不会单独存在,而是要依附在某个客户上,并且没有任何功能会从地址对象为起点。
2、在一个配送系统中,有可能客户是值对象,而地址是实体。因为配送系统关心的是货物和配送跟踪,而至于谁收货物则不是系统关心的东西。

简单说两句吧,纯属个人理解!
借一句古语当引子:止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得!

从大粒度下看,模型会分解成两大块:
1. 静态特征:domain 大体从描述业务数据开始进行驱动;
2. 动态特征:service 大体上是业务操作的集合,形成服务层。

有些操作会放在domain的类中, 那是常常是因为,数据与操作的生命周期都要受同一个对象生命周期的限制; 而有很多操作方法会放在service层类中, 那常常是因为, 数据与操作的生命周期不拘束在一个对象生命周期中,使用灵活,而且这些类型本身就是方法的集合,适合提炼出接口,做好层与层之间的分离。

从小粒度去看,类分为两大块:
1. 静态特征: 属性(数据)
2. 动态特征: 方法(操作)

考察属性和方法如果分离,就意味着数据与操作朝着各自更抽象更灵活的方向去发展,尤其方法,它适用数据的范围更广,重用性更强,继而演化或沉淀出方法(更大粒度看服务)不变,而应对千变万化的业务数据的架构来...
[该贴被youway于2010-04-21 23:57修改过]

当初我看到banq老师写的关于dci的文章,就考虑到如果能有相应的针对不同data对象(或者是业务对象)的context,那么很多问题就会迎刃而解,但是有发现对于置于某个context的业务对象,如果它随着不同context变化,那么势必要有非常庞大的接口支持(当业务上下文变化比较大,比较多时,尤其如此)

DDD的那本书,继续看下去,现在看到service,module,这些东西还是比较浅的,还要继续下去

如各位所言,不管是实体还是值对象,还是service,这些对象之间的业务区分不是非常明显,尤其是没有经过业务证明,就不是非常好区别

2010年04月20日 16:53 "Antinomy"的内容
鄙人以为,统一语言是需要和具体使用用户一起建立的。DDD强调抓住最核心的内容,让其在用户和开发人员心里有个统一的模型(毕竟需求变化大多来自使用的用户。。。)。太拘泥UML和建模工具往往会分散了注意力。所以DDD一书都没有用很正规的工具就画, ...

Antinomy 正解。
DDD是一种介于分析与编码之间的工具,要让分析与设计互相影响,同时不失去对系统业务核心的理解。

DDD用来做前期的需求和业务分析是不错,但是用DDD的方式来做开发,简直是自虐,我绝对不会逼自己和兄弟们用DDD的方式来开发项目,这样对项目来说会陷入危险的境地。如果自己对这个感兴趣的话,可以自己做个简单的留言版来试验下,这个是个人自由,但是软件开发是集体行动,所以考虑的因素会多些。如果感觉对象贫血,那你就把对象聚合下吧,而且还可以提供除set和get外的其他方法,这个完全是你的自由。还有如果用DDD,如何和spring比较完美的结合在一起?莫非又要回到factory的时代了。

2010年04月20日 11:17 "taochenpfj"的内容
.实体与值对象的区分:值对象,应该是比较好分割,我们只要把握住一点:免疫的,携带信息的无责任对象。那么对应的实体,就让我们现在比较迷惑,到底什么样的对象就应该做实体,到底什么样的实体应该做聚合根?现在的实体,从持久化层这个角度看,都是一个简 ...

说说我的想法,值对象不一定是无职责的对象。我目前判断值对象和实体的时候,主要是看是否需要跟踪,以及对象是否有独立的生命周期。如果一个对象有自己独立的生命周期,那么我觉得它就属于实体对象。

至于聚合跟的问题,我觉得这里也涉及到了封装和粒度的问题。我们涉及讲究对外封装,对内解构,当一个实体的行为变得太多的时候,我们势必要对其细粒度化,使得不同的对象负责不同的行为,但是细化以后,我们还需要封装,这就需要聚合跟了,聚合跟不仅要封装对子对象的访问,也要协调子对象来完成业务操作。


至于service等的,这其实还是与状态有关系,而我们的系统中一般存在两种状态,无状态和有状态,无状态的一般是service组件,或者一些技术性质的组件,而有状态的就是领域模型,因此这里就涉及到了生命周期管理的问题,无状态组件我们让容器管理,而领域模型需要我们自己手动管理,而我们要想管理生命周期,那么我们是必要先封装好对象的状态,因为整个生命周期中,我们所做的也就是对状态的跟踪。