Eric Evans关于技术如何影响DDD的会话

infoQ播放了DDD的创建者Eric Evans最新录像,谈了关于如今新技术对DDD的影响:Eric Evans on How Technology Influences DDD

我个人印象比较深入的是下面四点:

1.NoSQL和关系数据库一样,只是对象的一种持久方式。
关系数据库是最伟大的发明之一,关系模型也是看待问题最强大的工具,而面向对象也是一种看待问题的强大工具,但是如果你将两者捆绑在一起,实际是损毁它们的优点。

对象概念其实很长时间已经被J2EE等重量框架摧残了很长时间,而且更被阴险的映射到关系数据库上。

2.Domain Events或Event Sourcing实际是从面向函数角度去思考,面向函数语言的思维方式是一种流思考方式。值对象在面向函数中很自然,实际就是数据结构。

3.领域对象应该生存在内存中, 如果我们有一个巨大的内存存储,对象就能够成功生存,但是当你和关系数据库进行绑定等,你就会遭遇彼此的不匹配,如果你要得到快速的,好的 非常嫩脆的对象,就应该将他们放入内存中。

4.DDD最大特点是统一语言。

[该贴被banq于2012-09-13 16:18修改过]

个人想法:

这几天AAOSConf 真正召开,DDD和CQRS成为热门。

关于对象和关系数据库阻抗,本站一直在讨论,我在使用DDD开发Jivejdon开始时就坚持使用SQL,而当时ORM框架包括Hibernate很火,我也不是出于远见,而是让领域模型更加简单些,不要被ORM等配置给牵涉。如今大家都已经承认Hibernate代表的那段适合火红的岁月其实是一场越战。
Martin Fowler厌倦ORM了

对象应该生存在内存中,这是我当初开发JdonFramework的基本特点之一。

DDD最大特点是统一语言,统一语言其实也是统一思想,比如敏捷方法行为驱动开发BDD也是一种统一语言:行为驱动开发(BDD)如何与领域驱动设计(DDD)结合?,我觉得它的Given When Then模板非常实用,能够分解复杂的业务场景。

最近也讨论了:业务模型统一描述,我个人使用三个元素来表达统一语言:场景 事件和状态。

这三个元素每个后面都有一段隐式的定义:
场景:Context,可以理解为DDD的bounded context,也可以理解为DCI的Context,也对应于用例的Context,属于BDD中的Given。从逻辑上说,这是一段“假设”。

事件:表达对象之间交互的行为,对应于DCI的交互,也可以认为是BDD的When。我在业务建模:上下文(场景)还是服务?中提到,对象行为分为两种,一种是维护内部状态,一种是对外交互。前者通过DDD的聚合根这个结构角色确定下来;后者通过DCI中角色确定下来,虽然这两种行为分离,实际上是区分了不变和可变,可变的对外交互和场景角色有关,隔离了对实体对象的影响。
在这篇Event Sourcing + DDD带来的模型重构问题如何解决?,我认为正是忽视了这种分离,才导致实体的重构会影响交互性质的事件。

状态:状态和对象的内部行为有关,也是由DDD聚合根这个角色实施,如果实体之间没有从属关系,各自对自己负责,它们就可以称为聚合根。
状态是实体的状态,表达了实体参与了各种场景业务以后的结果。如果我们要还原某个业务场景发生前的状态和发生后的状态,只有实体状态是无法做到的,实体状态只能表达当前最新状态,历史状态基本不会记录,如果记录历史状态,不如记录历史事件更加精简,这样的事件可以实现重放和回放,这也是Event Sourcing意义所在。

总之:通过场景 事件和状态这三个关键词,我们可以抓住需求到架构代码实现的主要脉络,不至于错误表达需求,也不会无法灵活跟随需求,更不会因为代码自身重构带来架构变动。因为我们已经确定了建筑的钢架结构。

当然,实践中最难的是分辨出哪些是场景 哪些是对象的对外交互行为,哪些是维护状态的内部行为。


理论很丰富,但我觉得都只是点到即止,很难让人印象深刻或信服。个人认为还是拿些实际例子出来分析来的靠谱。

另外,我的帖子“Event Sourcing + DDD带来的模型重构问题如何解决”所表达的意思和你理解的不是一个意思。要知道有时虽然一个对象的结构没有变,但它的重要性或独立性可能会变化。比如同一个实体,之前是实体,后来我认为它应该独立,于是把它升级为聚合根,但是其结构完全没变化;另外,按照你的理解,聚合根用于维护内部状态,比较稳定,属于不变的部分,这个不认同,我们的聚合根为什么不会变?是因为只表示数据结构吗?谁说数据本身的结构或数据之间的结构不会变化?实际上很多变化都属于这类变化。也就是说,你说的用于维护内部状态的聚合根或实体的立场/独立性,或结构本身,或它们之间的关系都会变化,这种变化与如何与外界交互无关,即与Context无关,属于内在本质属性或内部对象结构的变化。
[该贴被tangxuehua于2012-09-15 01:07修改过]

2012-09-15 00:54 "@tangxuehua"的内容
这种变化与如何与外界交互无关,即与Context无关,属于内在本质属性或内部对象结构的变化。 ...

这个我很认同,如果你的需求经常变,那么直接映射需求领域中的领域模型,也就是实体内部属性会经常变化。还有一个情况,如果你的领域模型无法真正反映领域中本质,提炼得“中看不中用”(DDD书中语),那么领域模型当然也会变。

但是,如果我们的领域模型反映了需求,而且项目趋于结束,以我开发的jivejdon论坛为案例吧,已经五六年了,其ForumMessage和ForumThread之间结构很少变(不会发生今天ForumMessage映射到Forum,明天又改为和ForumThread,这种关系不怎么变。),当然ForumMessage的字段有增加修改,这不会影响大结构的数据字段变化是正常的。

虽然你是搞微软,但是有空可以参考一下Jdonframework,使用Jdonframework开发DDD+DCI+Domain Events真的很方便,完全是从领域模型角度出发的,而现有J2EE标准框架或Spring+Hibernate真的不适合DDD的开发,而估计微软世界和J2EE差不多。


2012-09-15 07:02 "@banq"的内容
Events真的很方便,完全是从领域模型角度出发的,而现有J2EE标准框架或Spring+Hibernate真的不适合DDD的开发,而估计微软世界和J2EE差不多。 ...

看来在对DDD的认识上还是有本质的区别,唯一能禁锢思想的是思想本身

我提一点我就这个问题的看法:

1、对于建模来说,刻意去区分对象行为属性的不变与可变意义不大,随着业务的深入不变的行为有可能变为可变的行为,甚至有些行为会消失或被若干更细粒度的行为取代,这都是一个正常的重构过程.
举个例子:订单上一般会有一个交货日期(Delivery Date),
对于电商订单系统,一两件商品一个订单,基本没有分批发货的情况,如果设计上可能直接把交货日期放在订单上面,如果有分批的情况也是拆成两个订单来处理,
对于外贸系统了,分批交货情况就很普遍了,因为设计到需要对这个订单的审核,客户要求、采购、生产供货能力评估,订单有不好做拆分,交货日期一般都放到订单明细上。
脱离具体业务场景,谈方式属性的可变或不变,真的一点意义都没有。

2、对象与关系数据库并不矛盾,他们都是用使用各自的“语言”来的表达同一东西,就是我们理解的世界一样,你可以从宏观的角度看,可以从微观的角度看,可以从分子世界看,可以重原子或亚原子的角度来理解世界。。。使用原生SQL或ORM都不重要,这只是架构层面的问题,不应该因为直接使用SQL或使用ORM就是领域模型变得简单或者复杂。

3、对于ORM,不是一个坏东西,架构层面简化了我们的工作,在业务层面上来将,提供了一个可以在关系数据库层面来验证业务模型的机会(同时具备两种思维建模能力),他们可以互补相互改进,在实践过程,经常会有这样的情景,有些隐藏的业务对象在分析的时候没被发现,它的属性方法被分散在其他不同的实体或服务之中,在ORM映射的转化过程,从关系数据层面找出这个隐藏的对象,进而完善我们领域模型。

4、上下文(场景):BoundedContext,经典的DDD好象没有这个元素,实践中感觉很有必要,强调的是外部与内部发生作用的一个边界,通过它如何来影响内部的行为。DCI的Context如果理解为用例的话,“不能把用例描述的功能等同于领域的功能”,用例表达的上在特定环境下系统与角色的交互,根据用例的级别有“海底”、“海面”、“浪花”、“白云”等级别,这些描述的一些功能只是系统的功能,但不一定全部都是领域层实现的功能,系统模型并不等于领域模型,系统实现的功能有相当于不分是架构实现的,与领域无关

5、记录历史状态我很赞同使用事件来完成,需要重建与回放的话,Event Sourcing 是架构层面很好的选择。。。

2012-09-15 07:02 "@banq"的内容
虽然你是搞微软,但是有空可以参考一下Jdonframework,使用Jdonframework开发DDD+DCI+Domain Events真的很方便,完全是从领域模型角度出发的,而现有J2EE标准框架或Spring+Hibernate真的 ...

呵呵,虽然我来自微软世界,但是我并没有用多少微软的东西,最多就用个IDE,还有基础的.NET框架。
我平时用的一般是开源框架或自己写的框架:Castle,NHibernate,公司内部的DDD开发框架,还有我自己写的Event Sourcing框架。

我会将一个模型同时支持两种模式:让领域模型既实现Event Sourcing,也实现与NHibernate的映射。要知道NHibernate可是很强大的,支持私有字段的映射,支持Component映射(可实现Value Object的映射)

另外,实际上你说的开发框架或模式是否方便不是一个人说了算的,要大家公认才可以,我也可以说我自己的框架很方便,用的多了唯手熟尔而已,没比较过就没结果。当然认为自己的东西用起来很爽很方便那是程序员的一个通病,包括我在内,呵呵。

总体上,我现在还是喜欢Event Sourcing,从我上次帖子中最后的回复你应该也可以知道,因为我也有我自己的想法和解决方案。但是我也不会否定ORM,我还会继续使用ORM,我觉得它和模型没冲突,因为我的模型不需要因为要和数据库表ORM而做出妥协。所以的映射都是在模型外围实现。再加上CQRS的架构,用于处理业务逻辑的用Model+Event Sourcing实现,同时在外围用Event Subscriber订阅事件,然后持久化显示表中的数据,然后显示数据用ORM查询得到,查询如果用NHibernate觉得慢,可以用Dapper,最快的查询ORM。总之,只要不要让数据库的东西影响模型的设计,就谈不上它们对模型的阻碍。

顺便问一下,如果你不用ORM,那你如何持久化或保存显示表中的数据?要知道Event Sourcing面向的是event,而event不是用于界面显示的,界面显示应该还有专门存放在显示表中的表达模型最新状态的实体数据。
[该贴被tangxuehua于2012-09-15 10:33修改过]

自己的ES框架?

能分享下是怎么做的吗?不知道你是怎么存贮一个event,我是用KV类型的LEVELDB做存储,我是把某个聚根的所有events当成value,好像一般的实现是一个event是一条数据。

自己的Es框架非常好啊。

cqrs或ES都属于面向函数范畴,侧重动作,形成动词流,这些都不涉及静态数据,而DDD侧重静态数据,一个实体既是ddd实体,又是orm实体,两者混合在一起,反而破坏面向对象和关系数据库各自优点。

这种对象和关系数据库阻抗本站讨论太多,说太多无益,那么实践中为什么有人声称两者都可以呢?强扭的瓜虽然不甜,但也是瓜吧?

ORM也许可以不屑与讨论,ORM的致命的缺点就在于在业务模型与数据关系模型之间建不存在必然的映射关系,试图建立联系必然导致其复杂度不可控制,MF只是点出原因;既然讨论很多,为什么没有人继续追问,“是什么导致业务模型与数据关系模型之间不存在必然的映射关系”?(我的看法而已)

ORM对于业务来讲,符合面向对象就用,不符合可以放弃掉,但没有必要因为某些极端的例子,就完全放弃,如果真正以业务为中心的话,ORM只是一个工具而已,合适的地方就用,不合适的地方用替代方案。

DDD实体还是那个DDD实体,可以是ORM的实体,也可以不是。。

2012-09-15 08:10 "@clonalman"的内容
我提一点我就这个问题的看法:

1、对于建模来说,刻意去区分对象行为属性的不变与可变意义不大,随着业务的深入不变的行为有可能变为可变的行为,甚至有些行为会消失或被若干更细粒度的行为取代,这都是一个正常的重构过程.
举个例子:订单上一般会有一 ...

1、不存在“刻意”去区分行为属性的变与不变,变与不变的思维是根本相异的,模型差异也是相当大。这两种认识很容易发现是世界观的不同,所以我认为这是一种从开始的思维选取差异,而不是后来去“刻意”区分,这表现为框架的选取。至于你说到的不变变成可变,是两者思维混合导致的,现在我还不能说这样混合好不好,因为这两者的相容性不多,不变性跟函数思维是同源的,而函数思维却不容下可变,这当中还需更多研究。

2、至于对象与关系数据库矛不矛盾,先来个问题:关系数据库其实已经包括处理关系了,它有自己的事务系统,但对象为什么还提出内存事务?

3、orm好不好,我认为:跟选择的思维有关。

4、上下文一词DDD中是有提到的,DDD提到的是聚合边界。用例与领域相异,我跟你拥有相同的观点。

5、无异议。

2012-09-15 10:22 "@tangxuehua"的内容
顺便问一下,如果你不用ORM,那你如何持久化或保存显示表中的数据?要知道Event Sourcing面向的是event,而event不是用于界面显示的,界面显示应该还有专门存放在显示表中的表达模型最新状态的实体数据。 ...

所有最新状态都在内存中,当不存在最新状态时,则重新用event进行运算得到最新状态放入内存。例如重启后,利用event重新运算出最新状态加载在内存中,而event大量计算问题用快照解决,可理解为运算的初态。

2012-09-15 15:59 "@SpeedVan"的内容
2、至于对象与关系数据库矛不矛盾,先来个问题:关系数据库其实已经包括处理关系了,它有自己的事务系统,但对象为什么还提出内存事务? ...

如果你的模型脱离关系数据库,实现内存对象,UnitOfWork的必须的自己去实现,使用关系数据库的话,UnitOfWork直接使用数据库的事务应该没矛盾.

聚合根的边界与上下文应该是两个概念.

使用ORM最大的担心是领域模型ER化,是典型的架构入侵领域的例子,使用是需要小心谨慎,显式入侵的话,尽量使用替代方案

我个人觉得讨论的意义不大

1.首先变或不变时相对的,这都是主观的,客观上,在有新的需求,原来不变也可能是要改变的, 另外我的觉这种变或不变是基于Domain的,和是实体还是聚根没有太大不关系。

2.我觉得Banq也没有说ORM和对象之间是矛盾的,只是可能有更好或者喜好的方式,如果让我选我会选ES.

我觉得讨论是好事,变味了就不好。

为什么这么多早泄的广告。。。。无语