暴露领域模型的不均匀性(中国IT读懂此文的不超过十个人)

08-07-05 GUANPEI
         

暴露领域模型(Exposed Domain Model)的不均匀性――一个问题引发的思考

一个简单的问题

对象关系映射持久化引擎提供了弱类型的查询OQL,一个业务实体Person,现在要查找名字为**的人。模型层有两个可能做法:

1.传统的Service-DAO: getPersonByName(String name)

2.由上层直接使用持久化引擎

问题本身虽然简单,但是不同的选择会反映出很深刻的方法论的倾向性。本文对它们作一些辨析。

深度暴露

上述问题的第二种处理方法实际上将面向对象的持久化引擎直接暴露给表现层(或者是逻辑层的最上部),第一种方法是对持久化引擎作了简单的封装;问题在于,这种封装过分降低了通用性,基本上是与功能用例形成对应关系,其后果是导致大量的无任何抽象性可言的垂直逻辑单元;它们随着功能的增加线性膨胀。第一种方案直接将持久化引擎(实际上是持久化引擎的一个很薄的封装)暴露出来,而持久化引擎提供了对业务实体最直接的访问能力,是一种最深层次的暴露。功能用例直接接触深层模型。

面向对象查询语言

第一种方案使系统的上部直接面向弱类型的面向对象查询语言而不是具有明确的语义的服务接口,其合理性问题需要一个像样的解释。首先,面向对象查询语言无论在语法结构上与SQL看起来如何相似,从概念模型的角度,它与SQL都有本质的区别:OQL所面向的是业务实体及其关联关系所形成的领域模型的静态结构,SQL所面向的是关系表;业务实体及其关系集中了两个成果:第一,对象关系映射引擎在抽象的、通用的、领域无关的层面上、语法的层面上所做的转化;第二,实体及其关联关系所反映的抽象的领域模型。如另外一篇文章所述,这个模型实际上是完整的领域模型的静态方面。这个模型的产生集中体现了设计活动的基础形成果。现实的开发者经常需要完成一种项目改造,将一个直接面向关系表构造行为逻辑的系统改造为通过对象关系映射构造行为逻辑的系统;如果一个开发者做过这种种遗留系统的改造,应该对这两点有所体会。对象关系映射工具所提供的由表结构到类的映射工具所得到的类型系统基本无法以一种直观的方式体现业务对象的关系,改造者需要经过大量的调整,在业务对象和遗留系统数据库模式之间建立曲折复杂的而不是直接的映射关系之后,对象层才能够呈现出业务逻辑概念模型;这种调整将占去改造工作的一半以上的工作量,从而也蕴含了一半以上的设计努力;业务实体及其关系在很大程度上基本反映出领域模型。通过OQL所操作的正是这样一个领域静态模型。

对领域静态模型的操作由模型本身来完成的时候,这种操作就内化为模型的行为,这也是面向对象的一个基础的概念:封装,使信息及其之上的逻辑合一。如同任何方法论一样,面向对象的封装概念同样有自己得适用条件,从而也会因为系统构造者的习惯、风格、价值观等因素的差异引发分歧;在客观上,封装的必要性问题也是一个连续的谱系,这个谱系的坐标轴就是:所封装的逻辑的强弱。逻辑越强,蕴含了相当的原子操作的组合操作时,封装是必要的,如果是不具有任何组合性质的读、写的原子操作,封装的必要性就会成为可选的问题从而引起不同的意见,不同的处理方式;这些处理方式的正确与否在抽象层面上难以界定,因而就需要根据所实施的环境和条件加以考量做出现实的判断。关于作为信息结构载体的Bean的成员是否应该直接暴露为公有变量还是应该通过一个方法来访问的争论,在抽象的层面上,与所谓暴露模型的问题一样,都是关于封装的争论。然而,Bean的问题的具体条件和后果都与暴露模型有极大不同;如果不考虑Bean工具类的因素,前者基本上会局限于代码风格这样比较狭隘的范畴。暴露模型则不同,对封装问题不同的处理所产生的设计后果的差异是非常巨大的,具体地说:过强的封装会严重降低模型的通用价值,从而产生组合爆炸般的封装代码。基于这一点,对于简单的、基本是原子级的对模型的操作,直接深入模型的深层,理论上具有根本的合理性。直接通过面向对象操作语言操作模型,在相当多的情况下都是正确的选择,它避免了大量的与数量极多并且变动不居的功能用例的平行的狭隘的结构组件的出现,使模型保持较高的抽象性、通用性、同一性。

模型的高级行为:组合逻辑

上一小节的分析实际上已经论证了组合逻辑作为模型的一个固有构成的必然性。这些逻辑如果由功能层基于领域静态模型来实现;换句话说,由功能层直接操作静态模型来实现组合逻辑,则会从实质上,而不仅仅是形式上违背封装原则,其具体表现是在不合适的地方,比如JSP页面或者Servlet中出现复杂的难于维护的QL语句;有时是较长的代码;使稀薄的控制层变厚重;这种现象也就是《POJO IN ACTION》中所描述的暴露模型的弱点(Also, the lack of a façade increases the chance that changes to the business tier could affect the presentation tier. There is also the risk of business logic creeping into the presentation tier.)。严格来说,这是教条主义的弱点,是对模型不分条件、不分部位、不加区分地暴露,是暴露过渡,是对封装的反动过度,矫枉过正;是方法实施者、系统设计者本人把握能力的问题。这是方法论之所以不能够代替设计者个人创造力、软件开发之所以成为Art的根源。

不均匀暴露

以上两个小节可以直接导出一个结论:模型在各个部分的暴露与不暴露、暴露到什么程度,是不均匀的,因为一个系统中不同的部分是否需要组合逻辑、逻辑组合的程度是不均匀的,正确地、辩证地处理这种不均匀性,自然会导致与之相适应的暴露程度。从这个意义上说其实也无所谓暴露模型或者封装模型,因为我们在一个现实系统的开发中无法彻底摈弃或者实施任何一种方法,设计者所要做的无非是权衡、折中,在正确的时间、正确的地点作正确的事情。因此,从现在开始,当我们在积极的意义上来谈论所谓暴露模型,所指的是这样一个不均匀的暴露模型

暴露模型中功能用例有时直探模型的核心,有时访问模型的表层,呈现出跨层访问的结构特点。

另外几个相关问题

领域实体及其关系通过QL语言暴露给功能层的前提是:这些实体及其关系形成了对概念模型的完备的再现;否则,如果实体集合需要经过复杂的装配才能够形成概念上的领域模型,则这种暴露就将导致功能层复杂的QL语句;这时,系统构造者面临两个选择:或者调整实体对象及其关系使其足够直观地再现概念模型,或者通过封装掩盖一个不良的静态模型。

适度封装(或者暴露)的指导原则可以被推广到其他方面,比如在处理应用逻辑与一个通用的、细粒度的API的关系上。但是,具体情况不同,在做法上差别极大,本文不作深入探讨。

暴露模型与开发流程

最后我们略谈一下暴露模型与开发流程方法论的关系。谈到开发流程方法论,在这里主要是指以重构为核心方法的敏捷开发和传统的事前设计为重点的方法。首先一点是,暴露模型与两类开发流程方法论之间没有绝对的关系;其次,由上文所述能够看出,暴露的不均匀性是一个需要极大创造性的指导原则,而不是设计教条,而一切创造性活动在本质上都更加倾向于一个进化的过程,因此,从这个意义上,暴露模型与敏捷开发、重构活动有天然的和谐关系;事实上,分布于模型的各个部位上的不均匀程度与模型本身的其他方面一样,伴随着开发的历史而形成自己变化发展的历史。

[该贴被GUANPEI于2008-07-06 06:06修改过]

         

freebox
2008-07-06 13:19

这需要这个模型与系统存在内在的耦合,spring之Mock可解决一些技术问题。如果我想把某些作业开放成服务供别人用,似乎不太合适。

banq
2008-07-07 09:58

>模型层有两个可能做法:

>1.传统的Service-DAO: getPersonByName(String name)

>2.由上层直接使用持久化引擎

Evans DDD中好像没有说模型层是这两种解决方式,所谓模型层就是围绕模型为核心展开,我一直主张:讨论设计领域的问题如果不从设计源头“分析建模”这个宏观高度来出发,就有可能发生为设计而设计。如果作者象Evans DDD那样举些例子来说明自己观点,所谓大道至简,相信你的思想会在中国IT普及。我们崇尚普及,中国软件设计太需要提高了。

[该贴被banq于2008-07-07 10:05修改过]

GUANPEI
2008-07-07 10:46

我并没有把问题描述清楚,因为这本来是写给公司开发团队的一篇东西,大家对我的想法都很了解,所以开头就是一提。基本是这个意思:功能层(大部分情况也就使表现层)是否为了一个极其简单的功能(getByName)都要做一个封装,而这个封装不具有任何逻辑?如果直接访问持久化引擎的一个简单封装,是不是更简洁,允不允许这样做?如下:

PersistenceFacade facade = ...//

List ps = facade.query("from Person where name = 'zhangsan'");

...

其中PersistenceFacade是一个O/R映射的简单封装

banq
2008-07-07 10:46

getPersonByName如何实现调用,需要视具体业务情况而定,如果getPersonByName是作为唤醒冬眠的领域对象一个方法,那么它和getPersonByID是等价的,同属于Evans DDD中从仓储Repository获取领域对象方法,可以使用工厂等方式解决。

如果属于一般查询,返回是一个集合,那么就要从过滤规则这方面去考虑。

4Go 1 2 3 4 下一页