0 概述
2004 年埃里克·埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design –Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design,简称 DDD)诞生;领域驱动设计这一理念迅速被行业采纳,时至今日仍是绝大多数人进行业务建模的首要方法。随着Martin Fowler 提出微服务架构[2],DDD也迎来了新的时代。
DDD 是一种架构设计的方法论;在具体落地的时候分为战略设计和战术设计,这两个阶段用于指导领域模型的设计和实现。DDD概念比较多比如:领域、子域、核心域、通用域、支撑域、限界上下文、聚合、聚合根、实体、值对象等等;这些名词,都是关键概念,但它们实在有些晦涩难懂,可能导致你还没开始实践 DDD 就打起了退堂鼓。因此,本文将结合自己工作理解对DDD相关概念总结。
1 战略设计
战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。
1.1 统一语言
定义:统一语言(Ubiquitous Language)是一种业务方与技术方共同使用的共同语言,业务方与技术方通过共同语言描述业务规则与需求变动,共同语言为双方提供了协作沟通基础。这里的业务方泛指一切非最终软件实现者(如客户、产品、业务、BI、UED等)。早期共同语言形式:用户画像(User Persona)、用户旅程能够从流程角度有效地构成共同语言,数据字典(Data Dictionary)是从软件实现侧形成的共同语言。
问题:DDD是一种模型驱动设计方法,为什么不直接使用模型模型作为统一语言呢?
直接使用模型作为统一语言,其效果并不理想主要原因有:1)业务方难以直接把模型和他们对系统的理解关联到一起,这主要是因为业务方看待系统的角度和开发人员不同,业务部方大多数习惯从业务维度去感知系统,如流程、交互、功能、规则、价值等去描述软件系统。2)模型则偏重于数据角度,描述不同了业务维度下,数据如何改变,以及如何支撑对应的计算与统计。3)模型是从已知的需求中总结提炼的知识,模型无法表达未知需求中尚未提炼的知识。此外统一语言可以作为中间的隔离层、提供句够的缓冲来帮助反馈模型的不足,同时也更能满足人与人之间的交流的需求。
统一语言案例:(PS:统一语言形式不重要,核心是统一语言要和模型关联,且需要多方认可&承认并达成一致)。
统一语言包含哪些东西:
- 领域模型;源自领域模型的概念与逻辑。
- 术语和概念;定义和约定一系列术语和概念,用于描述和表达领域中的事物、关系和行为。
- 限界上下文;围绕领域模型设置的边界。
- 规约和约束;对于领域模型和领域行为的规约和约束。
1.2 限界上下文
定义:限界上下文(Bounded Context)是一个显示的边界,用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。可以拆解为了两次词来理解,即限界和上下文;限界就是领域的边界,上下文则是语义环境。
Eric Evans 用细胞来形容限界上下文,因为“细胞之所以能够存在,是因为细胞膜限定了什么在细胞内,什么在细胞外,并且确定了什么物质可以通过细胞膜。”这里,细胞代表上下文,而细胞膜代表了包裹上下文的边界。
案例:我的一天建模,早上我乘坐地铁到公司上班,坐上地铁上我打开手机进入天猫好房喵屋会签到,然后我又打开微信读书读了会书并买了年卡,9点半之前到达公司开始工作。
不难看出下文文其实动态业务流程被边界(限界)静态切分的产物,所以产生了出行上下文、签到上下文等。每限界上下文提供不同业务能力,以满足当前上下文角色的目标;限界上下文划定领域知识的边界,形成了各自的知识语境。
案例:物流运输系统,该系统需要支持集装箱在铁路运输和公路运输的多式联运,需要计算每次多次联运的运费,以管理公司与委托公司的账目。
问题:系统定义运输上下文和财务上下文,运费计算是否可以放到财务上下文?如果从知识和能力的角度去理解,财务上下文的领域对象不具备计算运费的领域知识,不了解运输各种费用怎么计算的。缺乏这些知识自然就不具备计算运费的能力。如下图所示,财务上下文通过运输上下文,做账目结算,生成账单。限界上下文之间的复用体现对业务能力的复用而非对知识语境边界内的模型复用。
限界上下文特征:领域模型知识语境、业务能力纵向切分、自治的架构单元(最小完备、自我履行、独立进化、稳定空间)。
如下图所示,模块是从技术维度横向切分,再从领域维度进行纵向切分;限界上下文则是先从领域维度进行纵向切分,再从技术维度对限界上下文进行横向切分。
案例:供应链的商品模型,采购、订单、运输、库存都会用到商品信息(Product),但是他们所关心领域知识不一样。如果没有边界的,Product类就要包含与之相关的领域知识,使得Product领域模型变得越发臃肿。对于这种情况,可以根据限界下文拆分出不同的Product类,比如在采购上下文Product只会关心采购相关的领域知识。限界上下文告诉我们,同一个概念,不必总是对应于一个单一模型,也可以对应于多个模型。
1.3 上下文映射
定义:上下文映射是指将不同的限界上下文(Bounded Context)之间的概念和模型进行映射(Context-Mapping)和协调的过程。限界上下文之间映射,表达了不同领域之间如何进行交互和协同,领域之间的交互是一个复杂的问题,很多时候不仅仅和技术相关,更重要还会涉及到组织结构或者责任分配。比如:XXX领域为啥不按照我的要求提供数据,这个时候到底是谁问题呢,当我为多个领域提供服务时候,他们都对我提出很多要求。
案例:用户下单为例, 需检查商品库存量、提交订单时候需要锁定库存、和使用优惠。由此就产生了订单上下文和库存上下文、营销上文的协作必然带来领域知识传递;如果上游的提供的服务总在变化,订单开发者就需要采用一定的设计手段来避免服务变化带来的干扰。
DDD上下文映射主要有解决方案:共享内核、防腐层、开放主机服务、客户-供应方、遵奉者等。
1.4 核心域、支撑域、通用域
领域在不断细分&拆解不同子域,这些子域可以根据自身的重要性和功能属性,可以分为三大类,即:核心域、通用域、和支撑域,如下图所示,其中核心域是重心,支撑域和通用域为了支撑核心域的。值得说明是核心域只是一类统称,核心域可以包含多个功能子域。
核心域、支撑域和通用域的主要目标是:通过领域划分,区分不同子域在公司内的不同功能属性和重要性,从而公司可对不同子域采取不同的资源投入和建设策略,其关注度也会不一样。当然我们不能说支撑子域和通用子域是不重要的,它们也是重要的,只是我们对它们的要求并不像核心域那么高。
2 战术设计
战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软