我认为“正确的去思考与解决问题同时尽量不使自己陷入混乱的方式”就是“区分与归类,合理分配注意力”,详细一点就是:“分清主要的与次要的;分清哪些是相同的,哪些相似的,哪些是不同的;给予它们不同的的注意力,但要小心不要分散注意力”。这样做根本目的就是要“抓住事物的本质”,这是一种可以普遍的分析问题的方法,不限于程序设计,但就程序设计而言,它可以使我们尽可能的减少因变化而产生的麻烦,使建立的分析问题的模型更接近于事物的“真相”。“直指本心”这道理大家都懂,难的是如何在项目开发中拿出一套可行的方案,而DDD的价值就在于此,我觉得DDD的目标在于保持项目的“焦点”,“焦点不发散”便意味着“清晰”,“清晰”是项目正确且快速开发的基础(在这里“清晰”是相对于“模糊”、“混乱”来说的),即使项目很复杂,它也能比较容易的进行下去。DDD实现这个目标的方法就是将领域建模和设计开发融合起来,使设计者(开发者)的目光集中在领域模型之上,从而保持项目的“纯洁”。
Eric Evans在《领域驱动设计》一书中提出了一些模型和原则来指导解决实际问题,随着时间的推移,领域设计又有了一些新的发展,我也是正在学习的菜鸟,对这些理解不深,不敢多说,在这里仅就以前比较陈旧的概念进行一些分析。
业务领域主要由三种东西组成:实体、值对象、服务。实体具有唯一性,在其连贯的生命周期中,无论它的属性如何变化它都是唯一的,这在实现时就意味着实体要有唯一标识,无论在何时(产生后、生存变化过程中、消亡前)、何地(硬盘、内存、网络传输过程中)都可以通过标识找到它,并且从理论上讲,在同一时刻,无论是从什么地方(内存中、硬盘里还是在网络传输中)获得的有着同一标识的实体都是一样的;值对象是一种具有描述性质的对象,这意味着它没有标识,一般认为它是不变的,是可以共享的,当然也可以根据情况让它可变,但这时最好不要使它可共享。我认为划分实体与值对象的标准在于它们在当前领域模型中的地位,不是说地位高低决定是否为实体(虽然感觉上是这样),而是说它在模型中所扮演的角色是划分实体和值对象的重要参考标准。相同的一样东西在不同的模型中所被关注的方面和程度是不一样的,比如“汽车”,如果我们要建立一个城市中所有汽车的档案,那么“汽车”无疑是这个系统的重中之重,就要被设计成实体,它需要一个唯一标识来识别,不论它在不在当前城市、是否更换了发动机、是否被卖给了另一位车主,直到它被销毁,一个唯一的标识就代表着唯一的一辆汽车,跟随他完整的生命周期。而在一个出行管理系统中,交通工具一栏中所填的“汽车”就是一种描述,我们不需要知道倒底是哪一辆汽车,这时它就是值对象。业务领域中还有一类设计不属于实体与值对象,强行将它划入实体或值对象中会破坏模型,就像“交易”,强行将它划为买方或者卖方的动作都不妥,所以Eric将它划分为“服务”(这里所说的服务是指在领域层的服务,不是应用层或者基础设施层的服务),“服务”一般是没有内部状态的。 (右图是模型驱动设计语言的导航图,摘自《领域驱动设计.软件核心复杂性应对之道》)
定义实体、值对象和服务不是看起来那么容易的,尤其是在业务建模时对深层次的业务的理解、划分、关联、粒度的控制以及对某些方面的取舍。在复杂业务中建立一个合适的模型是非常难的,Eric在书中讲了一些例子,来介绍如何建模、重构,最终确定一个合适的模型,大家可以去看一下书,而我毕竟是一只菜鸟,经历过的项目很少,很难总结出一些通用的方法或规则,只能是具体问题具体分析了,关于这一点希望大家理解,如果可能的话大家可以拿一些自己遇到的复杂项目一起分析,让我们在讨论与争辩中共同进步。
实体、值对象与服务构成了领域模型,但使模型健康的运转还需要一些辅助的概念(它们是几种模式),这些概念虽然看起来与领域模型没有什么关系,但是不可或缺的,它们是:聚合、工厂和仓储。这三种模式的使用是为了“在生命周期中维护对象的完整性”[1],“避免模型由于管理生命周期的复杂性而陷入困境”[2]。
“聚合通过定义清晰的所有权和边界使模型变得更紧凑,避免出现盘根错节的对象(关系)网”[3]。“聚合”斩断了对象间不必要的联系,使关系密切的对象联系在一起,并使它们只有一个根,访问它们必需且只能先访问这个根。
“工厂”用来“创建和重组复杂的对象和聚合,并保持对其内部结构的良好封装”[4]。关于这个词相信大家已经很熟悉了,在绝大部分的设计模式的学习资料中第一讲总是“工厂模式”,它在领域模型中的作用就是新建以及重建对象。
“仓储用于处理生命周期的中间和结束部分,为我们提供查找和提取持久对象的方法,同时反与生命周期管理有关的复杂基础设施封装起来”[5]。“仓储”,顾名思义就是“存放对象的仓库”,它用一个统一的全局接口提供添加、删除、筛选对象的方法,即集中承担起对象的存储与访问工作,使客户的注意力集中在领域上。需要提一下的是,我们只需要为聚合提供仓储。
以上就是我对DDD的“快照”,简单的可以说是“简陋”了,没关系,我会在以后的笔记中一个个地详细分析它们。
[1]、[2]、[3]、[4]、[5]均摘自《领域驱动设计.软件核心复杂性应对之道》