如何快速入门领域驱动设计(DDD),DDD 架构及使用详解

一、背景与问题

无论是企业内部系统还是互联网产品,多年来开发这种基于业务与数据库的系统都是 IT 领域一个重要的内容。作为一个 IT 开发团队,无论是做外包还是自己的产品,都面临从产品经理拿到需求,然后需要进行两方面重要的工作:一是把需求转换为设计、二是从设计开始编写代码。

以前我们开发这种基于业务的软件产品,通常都是基于开发人员自己的经验和编写代码的习惯开始系统的设计与代码的编写。常见的方式是项目经理或架构师分析需求后,开始进行数据库表结构的设计,然后按照自己熟悉的分层架构,比如四层架构(数据访问层-业务逻辑层-WebApi-前端)进行代码组织。从上述的实现可以看出几个问题:

  1. 没有描述需求的设计模型;而是直接通过数据库表的方式体现,也就是需求与设计是脱节的。
  2. 编码的架构也没有与设计和需求对应起来。
  3. 业务逻辑与技术混在一起;业务逻辑可能直接调用的数据访问,这样把业务逻辑与数据访问的技术混在一起。
  4. 开发没有层次感和节奏感;系统没有一个统一的约束,开发人员没有一个统一的节奏,这主要体现在随意的编码。
  5. Bug 定位困难:当系统出现业务异常行为时,无法快速准确的定位出现问题的位置,因为系统不同开发人员的代码放置随意性。
  6. 需求变更响应缓慢:在大型的系统或产品中,当需要增加功能或修改现有功能时,因为代码架构的随意性,可能会出现改了功能可能会影响到其他的功能,造成系统极不稳定。 大家知道,万事万物都有其内在规律,这就是套路,软件设计与开发也是如此。软件设计与开发的套路就是领域驱动设计(简称 DDD)。DDD 这个套路能够灵活解决以上的问题,为我们提供一个好的设计、架构以及高质量的代码。

二、为什么要学习 DDD

DDD 是软件行业的一种成熟的方法论和模式。通过应用 DDD,我们能够很好的将需求应对到设计,能够让开发聚焦业务本身而不是技术,能够让代码体现我们设计,能够让团队在一个框架内有节奏的开发。

1. DDD 核心概念

首先给大家描述一下 DDD 的概念:DDD 是一种开发理念,核心是维护一个反应领域概念的模型(领域模型是软件最核心的部分,反应了软件的业务本质),然后通过大量模式来指导模型设计与开发。

从上述定义可以看出,DDD 方法首先是需要将需求分析后,形成一个反应需求的领域模型。领域模型就是大家平常理解的类、类的属性、类之间的关系等。当然在 DDD 中,为了更好的将领域模型反应需求,对类、类的属性、类之间的关系等有一些模式的指导。比如类的属性可能是一般属性,也可能是值对象;比如有关系的类之间是否是代表一个整体概念、有相同生命周期、需要统一持久化等。所以我们的领域模型除了能够跑通需求外,还要考虑聚合根、实体、值对象、聚合等概念的应用,这样领域模型的设计才能更好的反应需求,也能够更好的将设计对应成有约束力的代码。

另外 DDD 也提供了大量模式,告诉我们应该如何编写对应设计的代码,能够将我们的代码真正映射到设计;如何进行业务逻辑与持久化机制的剥离;如何进行更好的架构设计等。

2. DDD 能应对复杂性

在软件设计与开发过程中,复杂性主要体现在三个方面。一是技术维度,有业务代码的实现、有与数据库或其他持久化存储交互的实现、有消息队列的实现、有身份验证与授权的实现、有 WebAPI 暴露的实现等;二是业务维度,有太多的模块和功能需要去做;三是时间维度,需要快速的开发,快速的响应需求的变更,快速的修正 Bug。那么 DDD 是如何解决上述三个维度的复杂性呢?

  • a. 技术维度:通过合理的架构分层,能够让每层关注自己的事情,比如领域层只关注业务逻辑的事情,仓储实现层只关注持久化数据与查询的事情,应用服务层只关注协调领域层与仓储实现层完成用例的事情,接口层只关注暴露给前端的事情。
  • b. 业务维度:通过将大系统划分成多个界限上下文,可以让不同团队和不同人只关注当前上下文的开发。在当前界限上下文中的领域层、仓储实现层、应用服务层、接口层都与其他界限上下文独立开来,这样可以专注开发,并且在修改代码与发布产品时,影响面较小。
  • c. 时间维度:通过敏捷式迭代快速验证,快速修正。

三、什么人适合学习 DDD

从文章前面的部分大家可能有这样的初步感觉:DDD 是一套设计、架构和编码指导的方法,通过灵活运用 DDD,能够设计出很好反应产品需求的领域模型,另外通过更好的分层指导能够让我们的代码很好的反映设计,其实最终解决的问题就是我们第一个部分提出的 6 大问题。所以只要具有业务属性的产品或项目,在这个团队中的架构师、主要后端开发人员都是比较适合去学习 DDD 的。DDD 与语言无关,无论是 C#、Java 还是其他的语言,只要我们是遵循 DDD 套路的设计与编码架构,那就代表我们很好的实践了 DDD。当然为了你更好的入门 DDD,本文第五部分会有代码的演示。在代码演示部分使用了 C# 语言和 .net Core、EF Core、Unity、Asp.net Core WebAPI 等语言或基础框架,如果你是 Java 体系,也能够找到对应的框架实现的。再次强调,DDD 与语言和技术无关。

四、如何高效学习 DDD

其实要学习一种方法论和架构模式,就两个步骤:一是了解它核心的概念和组件,二是将这些概念和组件灵活的运用到产品开发中。

要高效的学习 DDD,首先我们要搞清楚两个方面的内容:一是核心的组件和概念,二是 DDD 分层架构。

核心组件和概念

  1. 界限上下文:首先要将大系统划分为多个界限上下文,比如一个经销商电商系统可以划分为产品、经销商、订单等几个界限上下文,每个界限上下文有自己的领域逻辑、数据持久化、用例、接口等。每个界限上下文根据特点,具体实现方式又不同,比如有些界限上下文基本没有业务逻辑,就是增删改查,则可以使用 CRUD 最简单的模式;有些界限上线文有一定的业务逻辑,但对高并发、高性能没要求,则可以使用经典 DDD 模式;有些界限上下文有一定的业务逻辑,而且有高性能要求,则可以使 CQRS 模式。 划分界限上下文并且在每个上下文实现自己的业务逻辑、持久化、用例和接口作用是巨大大,一是可以让不同开发小组专注与此界限上下文的开发,二是可以分别部署,三是如果一个界限上下文出现问题,并不影响其他界限上下文功能的使用。
  2. 实体:有业务生命周期的对象,采用业务标识符进行跟踪。比如一个订单就是实体,订单有生命周期的,而且有一个订单号唯一的标识它自己,如果两个订单所有属性值全部相同,但订单号不同,也是不同的实体。
  3. 值对象:无业务生命周期,无业务标识符,通常用于描述实体。比如订单的收货地址、订单支付的金额等就是值对象。值对象在数据库中表的表现形式可以是两种,一种是作为一个列或多个列与所属的实体对象所对应的表放在一起,另一种是单独一个表,通过ID与所属的实体对象关联。
  4. 领域服务:无状态,有行为,通常就是一个用例来协调多个领域逻辑完成功能。
  5. 聚合:通常将多个实体和值对象组合到一个聚合中来表达一个完整的概念,比如订单实体、订单明细实体、订单金额值对象就代表一个完整的订单概念,而且生命周期是相同的,并且需要统一持久化到数据库中。
  6. 聚合根:将聚合中表达总概念的实体做成聚合根,比如订单实体就是聚合根,对聚合中所有实体的状态变更必须经过聚合根,因为聚合根协调了整个聚合的逻辑,保证一致性。当然其他实体可以被外部直接临时查询调用。
  7. 服务:协调聚合之间的业务逻辑,并且完成用例。
  8. 仓储:用于对聚合进行持久化,通常为每个聚合根配备一个仓储即可。仓储能够很好的解耦领域逻辑与数据库。

DDD 经典分层架构

只有了解了经典 DDD 的架构,你才能知道具体在哪层要实现哪些功能,编写哪些代码,具体在开发支撑 DDD 的轻量级框架与具体模块代码实现时,才能做到有的放矢。

传统三层架构以及问题:
传统三层架构

问题:

  1. 过分注重数据访问层,而不重视领域。
  2. 业务逻辑直接与数据访问层耦合,与领域为核心的 DDD 思想背道而驰。
  3. 没有一系列的模式与方法论指导这种分层架构的开发约束。

经典 DDD 架构:
经典DDD架构

你可能感兴趣的:(如何快速入门领域驱动设计(DDD),DDD 架构及使用详解)