领域驱动设计概述

领域驱动设计(Domain Driven Design,DDD)是由 Eric Evans 最早提出的综合软件系统分析和设计的面向对象建模方法,如今已经发展成为了一种针对大型复杂系统的领域建模与分析方法。它完全改变了传统软件开发工程师针对数据库进行的建模方法,从而将要解决的业务概念和业务规则转换为软件系统中的类型以及类型的属性与行为,通过合理运用面向对象的封装、继承和多态等设计要素,降低或隐藏整个系统的业务复杂性,并使得系统具有更好的扩展性,应对纷繁多变的现实业务问题。

领域驱动设计贯穿了整个软件开发的生命周期,包括对需求的分析、建模、架构、设计,甚至最终的编码实现,乃至对编码的测试与重构。领域驱动设计强调领域模型的重要性,并通过模型驱动设计来保障领域模型与程序设计的一致。从业务需求中提炼出统一语言(Ubiquitous Language),再基于统一语言建立领域模型;这个领域模型会指导着程序设计以及编码实现;最后,又通过重构来发现隐式概念,并运用设计模式改进设计与开发质量。这个过程如下图所示:

领域驱动设计概述_第1张图片

这个过程是一个覆盖软件全生命周期的设计闭环,每个环节的输出都可以作为下一个环节的输入,而在其中扮演重要指导作用的则是“领域模型”。这个设计闭环是一个螺旋式的迭代设计过程,领域模型会在这个迭代过程中逐渐演进,在保证模型完整性与正确性的同时,具有新鲜的活力,使得领域模型能够始终如一的贯穿领域驱动设计过程、阐释着领域逻辑、指导着程序设计、验证着编码质量。

在为问题域寻求解决方案时,需要从宏观层次划分不同业务关注点的子领域,然后再深入到子领域中从微观层次对领域进行建模。宏观层次是战略的层面,微观层次是战术的层面,只有将战略设计与战术设计结合起来,才是完整的领域驱动设计。

先说战略设计阶段,如何划分子领域呢?针对问题域,可以引入限界上下文(Bounded Context)和上下文映射(Context Map)对问题域进行合理的分解,识别出核心领域(Core Domain)与子领域(SubDomain),并确定领域的边界以及它们之间的关系,维持模型的完整性。在子领域划分完成后,就可以针对不同的子领域选择不同的技术实现。通过分层架构来隔离关注点,尤其是将领域实现独立出来,能够更利于领域模型的单一性与稳定性;引入六边形架构可以清晰地表达领域与技术基础设施的边界;CQRS 模式则分离了查询场景和命令场景,针对不同场景选择使用同步或异步操作,来提高架构的低延迟性与高并发能力。

领域驱动设计并不牵涉到技术层面的实现细节,在战术设计阶段,主要应对的是领域的复杂性。领域驱动设计用以表示模型的主要要素包括:

  1. 值对象 Value Object:用于描述领域的某个方面本身没有概念标识的对象,值对象被实例化后只是提供值,值对象无状态无行为。
  2. 实体 Entity:与值对象不同,实体是有状态的,有演进的生命周期,实体本身会体现出相关的业务行为,业务行为会实体属性或状态造成影响和改变。
  3. 领域服务 Domain Service
  4. 领域事件 Domain Event
  5. 资源库 Repository:对领域对象生命周期的管理,负责从存放资源的位置(数据库、内存或者其他 Web 资源)获取、添加、删除或者修改领域对象。领域模型中的资源库不应该暴露访问领域对象的技术实现细节。
  6. 工厂 Factory:对领域对象生命周期的管理,负责领域对象的创建,往往用于封装复杂或者可能变化的创建逻辑。
  7. 聚合 Aggregate
  8. 应用服务 ApplicationService

Eric Evans 通过下图勾勒出了战术设计诸要素之间的关系:

领域驱动设计概述_第2张图片

战略设计会控制和分解战术设计的边界与粒度,战术设计则以实证角度验证领域模型的有效性、完整性与一致性,进而以演进的方式对之前的战略设计阶段进行迭代,从而形成一种螺旋式上升的迭代设计过程,如下图所示:

领域驱动设计概述_第3张图片

面对客户的业务需求,由领域专家与开发团队展开充分的交流,经过需求分析与知识提炼,以获得清晰的问题域。通过对问题域进行分析和建模,识别限界上下文,利用它划分相对独立的领域,再通过上下文映射建立它们之间的关系,辅以分层架构与六边形架构划分系统的逻辑边界与物理边界,界定领域与技术之间的界限。之后,进入战术设计阶段,深入到限界上下文内对领域进行建模,并以领域模型指导程序设计与编码实现。若在实现过程中,发现领域模型存在重复、错位或缺失时,再进而对已有模型进行重构,甚至重新划分限界上下文。

前面讲的是领域驱动设计的大致方法与流程,对于在实际项目里应用领域驱动设计,还有一些疑问:

  1. 什么是统一语言呢?
  2. 如果设计领域模型?有哪些典型的模型?
  3. 领域模型是如何指导程序设计的?
  4. 重构又是如何反馈到统一语言的?
  5. 对于子领域架构的实现,有哪些常见的架构方法呢?各自的优缺点、适用场景是什么呢?

你可能感兴趣的:(系统架构)