一、问题

对复杂软件的开发,比如一个财务系统,涉及多部门的人员沟通,财务人员,架构师,产品经理,开发人员,测试人员等等。很多时候,具有专业知识的领域专家不懂编程,开发人员需要缕清业务逻辑,但是认识事物都有一个过程,造成沟通困难。在开发过程中,才发现有些没有考虑到的场景,又要重新设计架构。这样反复修改软件,软件开发进度缓慢。

二、领域驱动设计(DDD)

领域驱动设计(英文:Domain-Driven Design,缩写DDD)是一种模型驱动的软件设计方法,通过领域模型捕捉领域知识,使用领域模型构造更易维护的软件。

当我们组建好了团队之后,应该从哪里开始?不是UI原型设计,不是架构设计,不是设计数据库,这些事情重要却非最高优先级。用正确的方法做正确的事情,运用领域驱动设计,要先识别问题域,进而为团队提炼达成共识的领域知识。

领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求;

1) 善于处理高复杂业务的产品研发、可帮助我们提炼稳定的产品内核(领域模型中称为核心域);

2) 通过建模可提高建模高内聚、降低模型间的耦合度,提高系统的可扩展性与稳定性;

3) 强调团队与领域专家的合作沟通,有助于建立一个沟通良好的团队组织;

4) 统一设计思想与设计规范,有助于提高团队成员的架构设计能力和面向对象设计能力;

5) 现有的微服务建构都是遵循领域驱动设计的架构原则;

6) 如果你负责的软件系统并不复杂,那么,你确实不需要学习领域驱动设计

三、DDD 要解决的两个核心问题:
​1. 如何合理的设计业务架构?
2. 如何使系统架构域业务架构保持一致并具备可持续的扩展?

领域驱动的设计目标:有效建模,并运⽤用领域模型驱动团队进⾏行行沟通分析、设计及开发。作为一种软件设计开发方法 :

  • 强调软件团队的组织和沟通方式
  • 强调采用演进式设计
  • 强调了统一的领域模型是核心资产
  • 在敏捷与复杂系统之间找到平衡

1.0版本的软件产出后,在运营过程中,不断的打补丁,迭代更新。渐渐的软件包从初识大小逐渐膨胀到无法维护。这时我们是推翻了重构,还是继续勉强运营?都不是很好的选择。为什么不在开始就建立一个好的模型呢?

所以DDD其实提倡的就是从模型开始设计,就要不断与业务契合。好的模型可以改善我们的认知,最有效的降低复杂度,提升对于变更的适应性,提升演进能力。所以,功能只是表象,模型才是内在。

四、领域驱设计的核心思想

1、模型与现实业务的统一
・模型和程序设计的核心相互影响。正是模型与实现之间的紧密联系才使模型变得有⽤, 并确保我们在模型中所进行的分析能够转化为最终程序。
・模型与实现之间的这种紧密结合 在维护和后续开发期间也会很有用,因为我们可以基于对模型的理理解来解释代码。
2、统一(业务和技术)语言
・模型是团队所有成员使用的通⽤语⾔的中枢。由于模型与实现之间的关联,开发人员可 以⽤该语⾔来讨论程序。
・他们可以在无需翻译的情况下与领域专家进行沟通。⽽且由于该语言是基于模型的,因此我们可借助自然语⾔对模型本身进行精化。
3、团队的持续学习,消化知识,系统持续的发展
模型是浓缩的知识。模型是团队一致认同的领域知识的组织⽅式
和重要元素的区分⽅ 式。透过我们如何选择术语、分解概念以及将概念联系起来,模型纪录了我们看待领域的⽅式。
当开发⼈员和领域专家在将信息组织为模型时,这一共同的语⾔ (模型) 能够促使 他们⾼效地协作。

五、DDD的一些概念

首先,我们要对面向对象的设计模式与方法,有一些基础才能理解以下内容。从泛化,实现,关联,聚合,组合,依赖这些概念,引出领域模型的描述语言。为什么有以下概念,因为为了实现统一的模型语言,需要对面向对象特有的概念进行了一层封装,请仔细体会以下概念。

在DDD中可以分为战略设计和战术设计,各自包含的内容如下图所示:

战略设计指的是对整个领域进行分析和规划,确定领域中的概念、业务规则和领域边界等基础性问题。在战略设计中,需要对领域进行全面的了解和分析,探究业务的规则和本质,并且需要考虑到领域的未来发展趋势和可能的变化。领域、子域和限界上下文属于战略设计的范畴。

领域

领域(Domain)其实就是一个组织所要做的整个事情,以及这个事情下所包含的一切内容。这是一个范围概念,而且是面向业务的(注意这里不是面向技术的,更不是面向数据库的持久化的),每个组织都有自己的人员、规则和流程,当你为该组织开发软件的时候,你面对的就是这个组织的领域。

子域

子域是指在一个大的领域中,可以进一步划分出来的独立的业务子领域,它们有着自己的业务概念、规则和流程等。为了区分重要性的不同,我们又会将子域划分成核心域、通用域以及支撑域。

核心域

决定公司和产品核心竞争力的子域就是核心域,它是业务成功的主要因素。核心域直接对业务产生价值。例如,在“RabbitAdvisors”中,订阅域就是核心域。

通用域

没有太多个性化的诉求,同时被多个子域使用的、具有通用功能的子域就是通用域。通用域间接对业务产生价值。例如,在“RabbitAdvisors”中,权限域、登录域就是通用域。

支撑域

支撑其他领域业务,具有企业特性,但不具有通用性。支撑域间接对业务产生价值,例如,在“RabbitAdvisors”中,专栏域、评论域就是支撑域。

限界上下文

限界上下文就是业务边界的划分,这个边界可以是一个子域或者多个子域的集合。如何进行划分,一个行之有效的方法是一个界限上下文必须支持一个完整的业务流程,保证这个业务流程所涉及的领域都在一个限界上下文中。限界上下文是微服务拆分的依据,即每个限界上下文对应一个微服务。

战术设计

战术设计则是在战略设计的基础上,对领域中的具体问题进行具体的解决方案设计。战术设计关注的是领域中的具体情境和场景,需要针对具体的问题进行具体的分析和设计,以满足业务需求。实体、值对象、聚合、工厂、资源库、领域服务和领域事件就属于战术设计的范畴。

基本概念

(1)实体(Entity):一个由它的标识定义的对象叫做实体。通常实体具备唯一ID,能够被持久化,具有业务逻辑,对应现实世界业务对象。

(2)值对象(Value Object):一个没有概念上标识符描述一个领域方面的对象,这些对象是用来表示临时的事物,或者可以认为值对象是实体的属性,这些属性没有特性标识但同时表达了领域中某类含义的概念。通常值对象不具有唯一ID,由对象的属性描述,可以用来传递参数或对实体进行补充描述。

(3)服务(Service):提供的操作是它提供给使用它的客户端,并突出领域对象的关系。所有的Service只负责协调并委派业务逻辑给领域对象进行处理,其本身并真正实现业务逻辑,绝大部分的业务逻辑都由领域对象承载和实现了。Service可与多种组件进行交互,当一个领域操作被视为一个重要的领域概念,一般就作为领域服务,服务是无状态的。

(4)聚合(Aggregate):用来定义领域对象所有权和边界的领域模式,是用来帮助简化模型对象间的关系。通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系网的形成。

(5)聚合根(Aggregate Root):每个聚合都有一个根对象(聚合根实体),从外部访问只能通过这个对象。根实体对象有组成聚合所有对象的引用,但是外部对象只能引用根对象实体。只有聚合根才能使用仓储库直接查询,如果根实体被删除,聚合内部的其它对象也将被删除。

(6)仓储(Repository):是用来管理实体的集合,仓储里面存放的对象一定是聚合,原因是领域是以聚合的概念来划分边界的;聚合作为一个整体概念,要么一起被取出来,要么一起被删除。外部访问不会单独对某个聚合内的子对象进行单独操作。因此,我们只对聚合设计仓储。

(7)工厂(Factory):用来封装创建一个复杂对象,尤其是聚合时所需的知识,是将创建对象的细节隐藏起来。如客户传递给工厂一些简单的参数,然后工厂可以在内部创建一个复杂的领域对象,然后返回给客户。

(8)领域事件(Domain Event):是将领域对象从对repository或service的依赖中解脱出来,避免让领域对象对这些设施产生直接依赖。领域事件的触发点在领域模型(domain model)中。就是当领域对象的业务方法需要依赖到这些对象时就发出一个事件,这个事件会被相应的对象监听到并做出处理。

六、DDD的实践

6.1 描述需求

需求描述要点:

(1)在对业务充分沟通和深入理解的基础上,从业务描述中提取名词,作为关键字;

(2)对关键需求点描述的格式应尽量表述为“用户+需求场景+操作对象”,并在团队之间达成共识;

(3)从提取出来的名词中总结实体,形成问题域;

(4)需求描述格式不限,可以用UML提供的方法和图例进行领域模型设计、确定模型之间的关系,也可以用其他传统方式,目标是团队成员理解一致即可。

用UML方法描述需求举例:

6.2、划分领域

(1)领域(Domain):领域就是问题域,一个领域可以拆分为多个子领域(Sub Domain)。

(2)核心子域(Core Domain):子领域中最核心的叫核心子域,团队的核心资源应该用在核心子域上,因为它是产品成败的关键。

(3)支撑子域(Supporting Subdomain):除了Core Domain外,其他的是支撑子域。

(4)通用子域(Generic Subdomain):有些支撑子域比较特殊,因为它解决的是一类通用问题,这类子域我们叫做通用子域。

  通用子域对应的限界上下文,通常会跨多个子域;多个子领域有时会有相交的部分,称作共享内核,体现到代码上就是同一份代码,在多个领域模型中复用,领域示意图如下:

6.3、用DDD写需求规格说明书

总结利用领域模型产出需求规格说明书的一般步骤:

(1)需求的理解和描述,抽取提炼关键字;

(2)根据需求划分出初步的领域和限界上下文,以及上下文之间的关系;

(3)进一步分析每个上下文内部,识别出哪些是实体,哪些是值对象;

(4)对实体、值对象进行关联和聚合,划分出聚合范畴和聚合根,进行建模;

(5)将每个业务场景流程化,按照场景流程,使用事件编排,定义好事件上下游关系;

(6)在工程中实践领域模型,并在实践中检验模型的合理性,倒推模型中不足的地方并重构。

参考:

领域驱动架构(DDD)建模中的模型到底是什么? – 知乎

一文教会你领域建模-CSDN博客

DDD-领域驱动设计 – 知乎
一文读懂:领域驱动设计DDD – 知乎