DDD 领域驱动设计
DDD的核心目的是为“高内聚,低耦合”提供一个可行办法。
微服务架构更强调从业务维度去做分治来应对系统复杂度,而DDD也是同样的着重业务视角。 如果两者在追求的目标(业务维度)达到了上下文的统一,那么在具体做法上有什么联系和不同呢?
我们将架构设计活动精简为以下三个层面:
业务架构——根据业务需求设计业务模块及其关系
系统架构——设计系统和子系统的模块
技术架构——决定采用的技术及框架
我们将通过上文提到的抽奖平台,来详细介绍我们如何通过DDD来解构一个中型的基于微服务架构的系统,从而做到系统的高内聚、低耦合。
首先看下抽奖系统的大致需求: 运营——可以配置一个抽奖活动,该活动面向一个特定的用户群体,并针对一个用户群体发放一批不同类型的奖品(优惠券,激活码,实物奖品等)。 用户-通过活动页面参与不同类型的抽奖活动。
设计领域模型的一般步骤如下:
根据需求划分出初步的领域和限界上下文,以及上下文之间的关系;
进一步分析每个上下文内部,识别出哪些是实体,哪些是值对象;
对实体、值对象进行关联和聚合,划分出聚合的范畴和聚合根;
为聚合根设计仓储,并思考实体或值对象的创建方式;
在工程中实践领域模型,并在实践中检验模型的合理性,倒推模型中不足的地方并重构。
https://tech.meituan.com/2017/12/22/ddd-in-practice.html
战略建模
领域
限界上下文
划分限界上下文
一个由显示边界限定的特定职责。领域模型便存在于这个边界之内。在边界内,每一个模型概念,包括它的属性和操作,都具有特殊的含义。
我们的实践是,考虑产品所讲的通用语言,从中提取一些术语称之为概念对象,寻找对象之间的联系;
或者从需求里提取一些动词,观察动词和对象之间的关系;我们将紧耦合的各自圈在一起,观察他们内在的联系,
从而形成对应的界限上下文。形成之后,我们可以尝试用语言来描述下界限上下文的职责,看它是否清晰、准确、简洁和完整。
简言之,限界上下文应该从需求出发,按领域划分。
上下文映射图
任何组织在设计一套系统(广义概念上的系统)时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。
限界上下文之间的映射关系
合作关系(Partnership):两个上下文紧密合作的关系,一荣俱荣,一损俱损。
共享内核(Shared Kernel):两个上下文依赖部分共享的模型。
客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。
遵奉者(Conformist):下游上下文只能盲目依赖上游上下文。
防腐层(Anticorruption Layer):一个上下文通过一些适配和转换与另一个上下文交互。
开放主机服务(Open Host Service):定义一种协议来让其他上下文来对本上下文进行访问。
发布语言(Published Language):通常与OHS一起使用,用于定义开放主机的协议。
大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。
另谋他路(SeparateWay):两个完全没有任何联系的上下文。
通过上下文映射关系,我们明确的限制了限界上下文的耦合性,即在抽奖平台中,
无论是上下文内部交互(合作关系)还是与外部上下文交互(防腐层),耦合度都限定在数据耦合(Data Coupling)的层级。
战术建模——细化上下文
实体
当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)
例:最简单的,公安系统的身份信息录入,对于人的模拟,即认为是实体,因为每个人是独一无二的,
且其具有唯一标识(如公安系统分发的身份证号码)。
值对象
当一个对象用于对事务进行描述而没有唯一标识时,它被称作值对象(Value Object)。
例:比如颜色信息,我们只需要知道{“name”:“黑色”,”css”:“#000000”}这样的值信息就能够满足要求了,
这避免了我们对标识追踪带来的系统复杂性
具有不变性、相等性和可替换性。
谨慎使用值对象
聚合根
Aggregate(聚合)是一组相关对象的集合,作为一个整体被外界访问,聚合根(Aggregate Root)是这个聚合的根节点。
聚合是一个非常重要的概念,核心领域往往都需要用聚合来表达。其次,聚合在技术上有非常高的价值,可以指导详细设计。
聚合由根实体,值对象和实体组成。
领域服务 一些重要的领域行为或操作,可以归类为领域服务。它既不是实体,也不是值对象的范畴。
领域事件 领域事件是对领域内发生的活动进行的建模。
DDD工程实现
模块 前文提到,领域驱动要解决的一个重要的问题,就是解决对象的贫血问题。
这里我们用之前定义的抽奖(DrawLottery)聚合根和奖池(AwardPool)值对象来具体说明。
领域对象 前文提到,领域驱动要解决的一个重要的问题,就是解决对象的贫血问题。
这里我们用之前定义的抽奖(DrawLottery)聚合根和奖池(AwardPool)值对象来具体说明。
资源库 领域对象需要资源存储,存储的手段可以是多样化的,常见的无非是数据库,分布式缓存,本地缓存等。
资源库(Repository)的作用,
就是对领域的存储和访问进行统一管理的对象。在抽奖平台中,我们是通过如下的方式组织资源库的。
防腐层 亦称适配层。在一个上下文中,有时需要对外部上下文进行访问,通常会引入防腐层的概念来对外部上下文的访问进行一次转义。
领域服务
上文中,我们将领域行为封装到领域对象中,将资源管理行为封装到资源库中,将外部上下文的交互行为封装到防腐层中。
此时,我们再回过头来看领域服务时,能够发现领域服务本身所承载的职责也就更加清晰了,
即就是通过串联领域对象、资源库和防腐层等一系列领域内的对象的行为,对其他上下文提供交互的接口。
数据流转
首先领域的开放服务通过信息传输对象(DTO)来完成与外界的数据交互;
在领域内部,我们通过领域对象(DO)作为领域内部的数据和行为载体;
在资源库内部,我们沿袭了原有的数据库持久化对象(PO)进行数据库资源的交互。
同时,DTO与DO的转换发生在领域服务内,DO与PO的转换发生在资源库内。
上下文集成
常见的手段包括开放领域服务接口、开放HTTP服务以及消息发布-订阅机制。
分离领域
浙公网安备 33010602011771号