模型驱动设计之构造块模式以及各模式关联方式(三)
前言
开发一个好的领域模型是一门艺术。模型驱动设计过程有一套标准模式,共用这些标准模式可以使设计有序进行,也使项目组成员能够更方便的了解彼此的工作内容。
下面是一张导航图,描述这些标准模式以及他们之间的关联方式:
围绕这张图展开对DDD过程中涉及的元素构造模式进行介绍。
分层分离领域(LAYER ARCHITECTURE)
要想创建一个能够处理复杂任务的程序,必须做到关注点分离——使设计中的每个部分都能得到单独的关注。并且在分离的同时也需要维护系统内部复杂的交互关系。
在DDD理论中,应该将一个系统粗略的分割为两个部分,一部分就是我们所说的领域,另一部分就是系统中的其他功能。分层的方式有多种,但根据经验和惯例,普遍采用LAYER ARCHITECTURE(分层架构),特别是有几个层已经形成了标准层。
LAYER ARCHITECTURE的基本原则是层中的任何元素都仅依赖于本层的元素或其下一层的元素,向上的通信必须通过间接的方式。
LAYER ARCHITECTURE的价值在于每一层都只代表程序中的某一个特定方面,这使得每个方面的设计都更具内聚性。
LAYER ARCHITECTURE的标准层(大多数成功的架构使用的都是下面4个概念层的某种变体):
LAYER ARCHITECTURE的各层之间通信:如上所述,分层架构只能是单向通信,上层依赖下层,方法是通过调用下层元素的公共接口。而下层元素与上层元素进行通信,则需要采用另一种通信机制,使用架构模式来连接上下层,如回调模式或者观察者模式。
基础设施层通常是以供上层调用的Service的形式出现的,但并不是所有的基础设施都是这种形式,有些技术组件被设计成直接支持其它层的基本功能(如为所有的领域对象提供抽象基类),并提供关联机制(如MVC框架)。
最好的架构框架既能解决复杂的技术问题,也能让领域开发人员集中精力去表达模型,而不考虑其他问题。然而使用框架很容易为项目制造障碍:要么设定了太多的假设,减少了领域设计的可选范围;要么是需要实现太多的东西,影响开发进度。
总结:
- 领域层是模型的精髓;
- 领域模型是一系列概念的集合。“领域层”则是领域模型以及所有与其直接相关的设计元素的表现,它由业务逻辑的设计和实现组成;
- 将领域层分离出来是领域驱动设计的前提;
反模式(Smart UI)
与Layer Architecture模式对立,此处提及反模式是为了帮助我们认清在什么情况下需要选择相对而言更难以实现的领域驱动设计模式。
Smart UI 优点:
- 效率高,短时间内实现简单的应用程序;
- 对开发人员能力要求不是很高;
- 可以快速迭代用户需求;
- 可以顺利使用关系型数据库,能够提供数据级的整合;
- 可以使用第四代语言工具;
- 移交应用程序后,维护程序员可以迅速重写他们不明白的代码段,因为修改代码只会影响到代码所在的用户界面。
Smart UI 缺点:
- 不通过数据库很难集成应用模块;
- 没有对行为的重用,也没有对业务问题的抽象;
- 快速的原型建立和迭代很快会达到其上限,因为抽象的缺乏限制了重构的选择;
- 复杂的功能难以扩展,只能增加一些简单的应用模块,没有很好的办法来实现更丰富的功能;
总结:
- Smart UI是基于用户界面层的需求进行的设计实现,没有考虑领域分离,后续难以维护,可扩展性差;DDD是基于模型进行的设计实现,项目越复杂,后续可扩展性越高,维护越简单;
- 领域驱动设计只有应用到大型项目才能产生更大的收益;
模型元素模式:Entity(又称为REFERENCE OBJECT)
本质:一种具有生命周期连续性,且由唯一标识而不非属性定义对象。
具体阐述:一些对象主要不是由他们的属性定义的。它们实际上表示了一条“标识线”(一个实体在整个生命周期可能会发生很多次属性变化,但不管怎么变,因为唯一标识的存在,这个实体始终还是它自己),这条线跨越时间,而且常常经历多种不同的表示,有时这样的对象必须与另外一个具有不同属性的对象相匹配(只要唯一标识一样,尽管对象间属性不同,但依旧属于同一个实体)。而有时一个对象必须与具有相同属性的另外一个对象分开(尽管属性一致,但唯一标识不同,就不属于同一个实体)。错误的不严谨的或者设计不合理的标识可能会破坏数据
标识设计原则:标识是Entity的一个微妙有意义的属性,模型必须定义出“符合什么条件才算是相同的事物”,即给出制定标识的规则。不管任何情况(包括分布式设计、数据库迁移、数据恢复等),都要保证实体唯一标识永远不会变化(比如人可以用身份证、社保号等国家公共体系下发的号码)
对象是否需要建模为实体?答:现实世界中的一个事物是否定义为Entity,取决于在领域分析过程中,是否需要将该事物进行唯一性定义,若需要则定义为Entity(比如体育场定座,一张票对应一个座位,这时就需要将座位定义为Entity;再比如体育场定座,采用入场券的形式,此时所有座位都一样,不具有唯一性质,这时座位就不需要定义为Entity;再比如银行转账交易,两笔交易记录的汇款方和收款方,以及转账金额都一样,此时只能依靠唯一标识“交易流水号”区分);
Entity建模指导:
- 抓住Entity对象定义的最基本特征,尤其是那些勇于识查找或匹配对象的特征上。只添加那些对概念至关重要的行为和这些行为所必须的属性,此外应该将行为和属性转移到与核心实体关联的其他对象中(这些对象可能是Entity,也可能是Object Value);
- 设计标识;
如何设计标识?
- 实体属性或者多个属性组合可以确立实体唯一性;
- 系统自动生成唯一标识(考虑分布式);
- 有时候自动生成标识只是用于内部需要,不需要对外展示;
- 有时需要对外展示(比如快递单号,可以跟踪快递运送轨迹);
- 有时候需要确保唯一标识在多个计算机系统之间具有唯一性(这样的系统通常需要使用由另外一家机构,一般是政府机构发放的标识符);
- 在任何情况下,当应用程序需要一个外部ID时,都由系统的用户负责提供唯一的ID;
模型元素模式:VALUE OBJECT
本质:是一种临时性的对象,仅表示事物的某种特征;
概念:用于描述领域的某个方面而本身没有概念标识的对象成为值对象;
对象是否需要建模为值对象?答:取决于这个对象在整个领域分析过程中是作为主体核心存在还是作为别的主体核心对象的附加属性存在,如果是后者则建模为值对象;
VALUE OBJECT的组成:① 可以是其他对象的集合;② 可以引用实体;
VALUE OBJECT的使用:① 经常作为参数在对象间传递消息,临时对象; ② 值对象应该设置为不可变对象 ③ 值对象所包含的属性应该形成一个概念整体(如下图,street、city、postal code不应该作为Person单独的属性,而是整个地址Address的一部分,Person再引用Address)
VALUE OBJECT设计原则:复制或共享或VALUE OBJECT保持不变
VALUE OBJECT为性能优化提供了更多选择,因为VALUE OBJECT为数众多。复制和共享哪个更划算取决于实现环境,以下情况最好使用共享:
- 节省数据库空间或者减少对象数量是一个关键要求;
- 通信开销很低时;
- 共享的对象被严格限定为不可变时;
只要VALUE OBJECT是不可变的,值对象就可以自由的共享
特殊情况:何时允许可变性
保持VALUE OBJECT 不变可以极大的简化实现,并确保共享和引用传递的安全性。而且这样做也符合值的意义。如果属性发生变化,我们该用一个新的VALUE OBJECT,而不是修改现有的VALUE OBEJCT。尽管如此,某些情况下处于性能考虑,仍需要让VALUE OBEJCT是可变的,这包括以下因素:
- 如果VALUE 频繁改变;
- 如果创建和删除对象的开销很大;
强调:如果一个VALUE的实现是可变的,那么就不能共享它。无论是否共享VALUE OBJECT,在可能的情况下都要将他们设计为不可变的。
对象关联
对象之间的关联使得建模与实现之间的交互更为复杂。从仔细地简化和约束模型的关联到MODEL-DRIVEN DESIGN,还有一段漫长的探索过程。现在我们转向对象本身。仔细区分对象可以使得模型更加清晰,并得到更实用的实现;
由于值对象没有标识,我们应完全消除值对象之间的双向关联关系;
模型元素模式:Service
概念: 有些重要的领域操作无法放到ENTITY和VALUE OBJECT中。有些操作从本质上讲是一些活动或动作,而非事物,但由于我们的建模范式是对象,因此要想办法将之归为对象,这就是Service的由来;
定义:Service名称应该来自于UBIQUITOUS LANGUAGE,参数和结果应该是领域对象;
使用:当领域中的某个重要的过程或转换操作不是ENTITY或VALUE OBJECT的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为Service。定义接口时要使用模型语言,并确保操作名称是UBIQUITOUS LANGUAGE中的术语。此外,应该使Service成为无状态的。
说明:Service是技术框架中的一种常见模式,但它们也可在领域层中使用,我们要注意区分属于领域层的Service和其它层的Service,并划分责任,以便将他们明确分开;
领域层Service和应用层Service之间的关系:
- 领域层Service可以控制领域层接口的粒度,并且避免客户端与领域对象发生耦合;
- 与应用层Service:应用层Service负责对领域对象之间的行为进行协调,而领域层Service是应用层Service和领域对象之间交互的必经之路;
注意:任何其它层想要访问领域对象必须通过领域Service进行访问;
模型元素模式:MODULE(也称为PACKAGE)
本质: 合并同类项(依据领域概念进行划分模块)
好处:MODULE高内聚低耦合,可以让参与者在MODULE中查看细节而不至被整个模型淹没;亦可使参与者在MODULE之外观察MODULE之间的关系而不用关注细节;
说明:MODULE与其它模式一样,要与模型共同演进,当发现MODULE不合理时,应同步更新模型并重构MODULE,强调无论在视线中采用哪种开发技术,我们都要尽一切办法来减少重构MODULE的工作量