记一次公司内部技术分享—DDD
前言
笔者于2021年入职了杭州一家做水务系统的公司,按照部门经理要求,新人需要做一次个人分享(主题随意)。 当时笔者对DDD充满了浓厚的兴趣,之前也牛刀小试过,于是就决定班门弄斧Show一下。后来在公司的新项目中,笔者也是大胆启用了DDD,带领团队和DDD来了一场亲密拥抱。尽管该项目最后没有机会落地,笔者却未留下任何遗憾。今天拿来记录,权当是本人对DDD的一次追忆吧!
正题
领域驱动设计(Domain Drive Design)的概念,以下简称“DDD”,来源于2003年著名建模专家Eric Evans发表的他最具影响力的书籍《领域驱动设计--软件核心复杂性应对之道》。DDD是一种设计方法,围绕业务概念构建领域模型,并通过分离技术实现的复杂度,从而控制软件演化的复杂度。
什么是领域
领域(Domain)是指一种特定的范围或区域。领域是有范围的,可以根据范围的不同来定义边界。DDD的领域指的就是这个边界内要解决的业务问题域。
什么是子域
领域可细分为多个子领域,即子域(Sub Domain)。每个子领对应一个更小的问题域。子域可根据自身重要性和功能属性划分为三类子域:
核心子域:决定产品和公司核心竞争力的子域,是业务成功的主要因素和公司的核心竞争力。
通用子域:没有太多个性化需求,同时被多个子域使用的通用功能子域。比如: 认证、权限等。
支撑子域:既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,但又是必需的子域。具有企业特性,但不具通用性。
什么是战略设计和战术设计
DDD分为战略部分和战术部分,两者相辅相成。战略部分用于理解、梳理业务,找到核心业务,更好地划分系统(这也是DDD为什么可用于指导微服务设计的原因)。战术部分用于落地到代码上,用代码来清晰地表示业务,代码如何分层、如何设计都有一套成熟的指导方案。
什么是通用语言
通用语言(Ubiquitous Language) 是通过团队交流达成共识的能够简单、清晰、准确传递业务规则的语言。通用语言往往跟领域中的名词术语和用例场景有关。名词一般可以给领域对象命名,如:客户、订单等,它们对应领域模型中的实体对象。动词则表示一个动作或领域事件,如:商品已下单、订单支付等,它们对应领域模型中的领域事件或者命令。
什么是限界上下文
限界上下文(Bounded Context) 可以拆分为两个词:限界和上下文。限界是指一个边界,某一个范围。上下文:即语义环境。限界上下文就是在限定的上下文环境内,用来封装通用语言和领域对象,保证领域内的一些术语、领域对象等有一个确切的含义,没有语义二义性的一个业务边界。
什么是领域模型
领域模型(Domain Model)是关于某个特定业务领域的软件模型。通常,领域模型通过对象模型来实现,这些对象同时包含了数据和行为,并且表达了准确的业务含义。
什么是实体
在DDD的领域模型中有这样一类对象,它们拥有唯一标识符,并且它们的标识符在经历各种状态变更后仍能保持一致。对于这些对象而言,重要的不是属性,而是其延续性和标识。我们把这样的领域对象称为实体(Entity)。
什么是值对象
值对象(Value Object) 是通过对象属性值来识别的对象,它将多个相关的属性组合成一个概念整体,用于描述领域的某个特定方面,是一个没有标识符的对象。值对象是没有生命周期的,且数据不可变。
什么是聚合和聚合根
聚合(Aggregate)定义了一组具有内聚关系的相关对象的集合,我们把聚合看作是一个修改数据的单元。每个聚合都含有一个根实体,叫做聚合根(Aggregate Root)。
1. 每个聚合有一个根和一个边界,边界定义了一个聚合内部有哪些实体或值对象,根是聚合内的某个实体。
2. 聚合内部的对象之间可以相互引用,但是聚合外部要访问聚合内部的对象时,必须通过聚合根才行。
3. 聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可,因为它们总是从属于这个聚合的。
4. 聚合根负责与外部其他对象打交道并维护自己内部的业务。
5. 基于聚合的以上概念,我们可以推论出从数据库查询时的单元也是以聚合为一个单元,也就是说我们不能直接查询聚合内部的某个非根的对象。
6. 聚合内部的对象可以保持对其他聚合根的引用。
7. 删除一个聚合根时,必须同时删除该聚合内的所有相关对象。因为它们都同属于一个聚合,是一个完整的概念。
什么是仓储
仓储(Repository)是一个协调领域模型和数据映射层的组件,使用类似集合的接口访问领域对象。
1. 仓储是介于领域层和基础层之间薄薄的一层,为了解耦领域逻辑和数据处理逻辑。
2. 仓储模式包含仓储接口和仓储实现。仓储接口定义在领域层,而仓储实现在基础层(依赖倒置)。
3. 仓储是为聚合服务的,一个聚合会对应一个仓储,统一由仓储来完成聚合数据的持久化。
什么是领域服务
当一块业务逻辑无法融入任何现有聚合,而聚合又无法通过重新设计适应操作时,就要考虑使用领域服务(Domain Service)了。为了实现业务操作,领域服务负责协调聚合和仓储的活动。同时领域服务使用的名字严格遵循统一语言。
DDD如何分层
基础设施层:该层为其它各层提供技术有关的任何东西,它不会涉及任何业务知识。比如:对数据库的访问,因为数据的读写与业务无关。
领域层:包含了业务所涉及的领域对象、领域服务以及它们之间的关系。这部分内容的具体表现形式就是领域模型。DDD提倡富领域模型,即尽量将业务逻辑归属到领域对象上,实在无法归属的部分则以领域服务的形式进行定义。
应用程序层:该层不包含任何领域逻辑,但它会对任务进行编排,并可以维护应用程序的状态。因此,它更注重流程性的东西。该层是针对特定应用程序的。
表现层:负责提供用来完成任务的用户界面。它是一组屏幕,每个屏幕都包含了一组数据以及从这个屏幕发送另一组具有明确定义的数据的操作。
为什么需要DDD
即使我们的软件中没有bug,也不能表示我们设计的软件模型本身就是好的。使用DDD能够让我们的软件设计更加合理,但不止于此。对一个业务复杂的系统而言,使用DDD有如下好处:
1. 开发者和熟悉业务的人一起工作,加强团队间不同角色的合作。
2. 能够帮助业务人员和开发人员梳理清楚复杂的业务规则。
3. 开发出来的软件是能够准确表达业务规则的,设计就是代码,代码就是设计。
结尾
以上就是笔者那次PPT分享文字,差不多一个多小时的阐述,也只能让大家对DDD有个浅显的认识。 笔者第一次接触DDD的时候也是一头雾水、不知所云,觉得这东西太高大上。后来通过拜读园子里大佬的关于DDD的文章,再加上自己的反复推敲和琢磨,才有了一丝入门的感觉。事实上,光有理论知识还不够,你还等有一把神兵利器来为你的DDD项目保驾护航。没错,笔者正是在项目中借助了ABP这套框架,才有了足够的开发底气。 最后用一句话总结就是:纸上得来终觉浅,绝知此事要躬行。