领域驱动设计之实践与反思
领域驱动设计之实践与反思
一、引言
前两三年,在这里我先后写过三个帖子,分别阐述了对三个问题的思考。
1)什么是程序?结论是:程序=数据结构+算法+设计模式。
2)什么是领域模型?结论是:人对领域的认知,即其心智模型。
3) 如何描述领域模型?结果是提出一套建模原语:模型、结构特征、行为特征和场景。
这些帖子都曾引起了一些争论或批评,之后我也在不断实践和反省,今天我决定把最近一两年实践的经验,加以总结,在此欢迎各位的讨论和质疑。
二、正文
首先,对上面这个图进行解释。
1、业务模型 = 业务对象 + 业务规则。
2、业务场景:在业务场景中,业务对象在业务规则的约束或指导下,呈现依赖于场景的行为特征和结构特征。
2.1)行为特征:比如监听请求事件、反序列化操作、请求参数校验、抛出异常等等,主要描述业务对象在特定的业务场景下的行为。
2.2)结构特征,比如获取事件处理结果、序列化操作、响应数据格式、业务对象状态显化等等,主要是描述业务对象在特定的业务场景下的结构。
3、接口层:接口层向上为业务场景提供服务,向下使用数据库提供的服务。领域模型通过接口层与外界交互。
4、REST API: 如果说接口层是业务模型与Web服务内部其它部分的交界处;那么REST API则是Web服务与外界的交界处,REST API描述如何开放Web服务给Web应用。REST API调用,使得应用端进行“表述性状态的转移”,服务端进行“业务场景的切换”。
接着,描述这个图的用法,这也是我目前使用的设计与实现的方式。
1)从面向应用或用户代理的API开始(粗粒度的REST API,HATEOAS);
2)接着是场景设计,包括结构特征和行为特征,一个REST API可以表示一个或多个类似的场景;
3) 根据场景,设计业务对象和业务规则(关注点:如何保证其在场景过程的可用性)
4)根据场景,设计数据表结构(关注点:如何场保证对场景结果的持久性)
5)接口层的设计,last but not least, 考虑在业务场景、业务模型(业务对象和业务规则)、业务数据(表结构)之间,如何有效地将信息流(数据流和控制流)进行传递或转移。
注:步骤3和4,设计的先后的顺序没有关系,事实上两者可灵活交替设计都无妨。因为两者的关注点是有差别的,但又相互补充。业务数据的设计追求持久性和无冗余性,而业务模型的设计着眼于可用性和直观性。
三、反思
1、关于ORM
如何将表的关联(one-to-one, one-to-many, many-to-one, many-to-many)映射到对象上,如何将对象的继承映射到表(single table、joined、table per class),这种费脑子的事情且不说。ORM的性能也是大大的问题,比如当我们只要记录的某几个属性的数据,且不需要级联的数据,ORM却不分青红皂白,把所有数据都返回给我们。
JPA是ORM的集大成者,巅峰之作,也并没有从根本解决上对象和关系的天然阻抗。当我看到业务对象填满了各式各样的annotation,总觉得该是哪里出错了。
ORM真的是对程序员智力的极大浪费。不管正向设计(从对象到表),还是逆向设计(从表到对象),都是错误的方向,这条路不适合再走下去了。
事实上,在数据的访问上,我们需要的,仅仅是一个小小的库, 帮助我们完成连接的管理和结果集数据的自动提取而已。
在我的设计中,接口层可以直接读写取数据库,如果业务场景需要的话,也可以使用异步的方式进行读写。
2、关于MVC和DCI范式
在经典J2EE三层架构,MVC一般在表现层应用,jsp为视图,servlet为控制器,java bean为模型。jsp是in-out的servlet,是服务端的脚本,把数据的展示,客户端该干的活都干了,真是越俎代庖呀。Ajax的出现,这一问题得以缓解, 服务端只要给客户端数据即可。jsp等服务端的渲染技术,该进博物馆,退出历史舞台了。
MVC在Web架构中为什么会被这样误用呢? 事实上,MVC在桌面软件或客户端应用的构建上非常非常好用。我猜测,J2EE架构的最初设计者,并没有认真考核其是否适合构建Web服务。
那么DCI适合构建Web服务吗?恐怕也不太适合,其适用于指导业务场景的设计与实现,而Web服务的整体架构,还是REST风格比较合适。
3、Web服务不是Database应用
在jdon上可能存在这样一个误区,老是想把数据库给屏蔽了。ORM这条路已经行不通了,现在开始换另一条路NoSQL。可是诸君可见?所谓的NoSQL, 却追求着SQL-style query interface。从使用上说,API style是给我们最直接的感受,至于底层是使用key-value方式还是relation的方式实现,对我们的影响到底有多大呢?
事实上,一味地追求屏蔽数据库,极有可能走向另一个极端:我们所构建的Web服务不过是一个Database的应用。
我想说的是,不要像ORM那样试图去屏蔽数据库,记住Web服务的真正使命: 为Web应用提供服务,其它东西任何都要在其之下;与其浪费精力去屏蔽数据库,不如将其视为构建Web服务的一部分,发挥其最擅长的能力;而将更多地精力花在如何给Web应用提供更友好的API的设计上。