概要
领域驱动设计并不是一套死板的方法,而是一种设计思想、一种开放的设计方法体系。
可在其中引入有利于领域驱动设计的实践的任何事物:
- 新概念:六边形架构、领域事件、事件溯源(Event Sourcing)、事件风暴(Event Storming)。
- 建模:用例(Use Case)、测试驱动开发(TDD)、用户故事(User Story)建立模型。
- 表达模型:面向对象编程(Object Oriented Programming)、函数式编程(Functional Programming)编程范式。
-
DDD 和微服务
主要体现在领域驱动设计中限界上下文与微服务之间的映射关系。假如限界上下文之间需要跨进程通信,并形成一种零共享架构,则每个限界上下文就成为了一个微服务。
DDD 未来趋势
未来 DDD 的发展可能会出现以下趋势:
以函数式编程思想为基础的领域建模理念与事件驱动架构和响应式编程的结合,可能在低延迟高并发的项目中发挥作用。这种领域驱动设计思想已经比较成熟,但目前还没有看到太多成功的运用。
以 DDD 设计方法为基础的框架的出现,让微服务设计与领域建模变得更加容易,降低领域驱动设计的门槛。
面临的问题
团队内外成员之间的协作与沟通
只有解决了团队内外成员之间的协作与沟通,才能更好地进行领域驱动设计。
正确地识别限界上下文
Vaughn Vernon:“By experience!”
专栏组成
战略:架构
- 战术:设计与编码
实践:函数式编程(EDA、CQRS、Domain Event)
学习建议
学习建议:
积累领域知识,以提高沟通与协作能力;
- 以 Eric Evans 的《领域驱动设计》为主体,广泛涉猎与 DDD 相关的书籍与文章,并关注 DDD 社区的最新知识;
- 要善于总结,理清 DDD 中各个概念之间的区别与应用场景。
打造自己的技术标签,寻找和定位自己的技术发展方向,成为这个方向的技术专家。
开篇
战略设计
覆盖了领域建模分析与架构设计的战略设计过程,从剖析软件复杂度的根源开始,引入了领域场景分析与敏捷项目实践,帮助需求分析人员与软件设计人员分析软件系统的问题域,提炼真实表达的领域知识,最终建立系统的统一语言。
将主流架构设计思想、微服务架构设计原则与领域驱动设计中属于战略设计层面的限界上下文、上下文映射、分层架构结合起来,完成从需求到架构设计再到构建代码模型的架构全过程。
第一部分(第 3~7 篇):软件复杂度
- 领域驱动设计的目的是应对软件复杂度。本部分内容以简练的笔触勾勒出了领域驱动设计的全貌,然后深入剖析了软件复杂度的本质,总结了控制软件复杂度的原则,最终给出了领域驱动设计应对软件复杂度的基本思想与方法。
第二部分(第 8~12 篇):领域知识
- 领域驱动设计的核心是“领域”,也是进行软件设计的根本驱动力。因此,团队在进行领域驱动设计时,尤其需要重视团队内外成员之间的协作与沟通。本部分内容引入了敏捷开发思想中的诸多实践,并以领域场景分析为主线讲解了如何提炼领域知识的方法。
第三部分(第 13~22 篇):限界上下文
- 限界上下文是领域驱动设计最重要的设计要素,我们需要充分理解限界上下文的本质与价值,突出限界上下文对业务、团队与技术的“控制”能力。
- 提出了从业务边界、工作边界到应用边界分阶段分步骤迭代地识别限界上下文的过程方法,使得领域驱动设计的新手能够有一个可以遵循的过程来帮助识别限界上下文。
- 剖析上下文映射,确定限界上下文之间的协作关系,进一步帮助我们合理地设计限界上下文。
第四部分(第 23~30 篇):架构与代码模型
- 作为一个开放的设计方法体系,本部分引入了分层架构、整洁架构、六边形架构与微服务架构等模式,全面剖析了领域驱动设计的架构思想与原则。
- 结合限界上下文,并针对限界上下文的不同定义,对领域驱动的架构设计进行了深度探索,给出了满足整洁架构思想的代码模型。
第五部分(第 31~36 篇):EAS 系统的战略设计实践
给出一个全真案例——EAS 系统,运用各篇介绍的设计原则、模式与方法对该系统进行全方位的战略设计,并给出最终的设计方案。
价值
领域驱动设计尤其善于处理与领域相关的高复杂度业务的产品研发,通过它可以为你的产品建立一个核心而稳定的领域模型内核,有利于领域知识的传递与传承。
- 强调团队与领域专家的合作,能够帮助团队建立一个沟通良好的团队组织,构建一致的架构体系。
- 强调对架构与模型的精心打磨,尤其善于处理系统架构的演进设计。
面向对象设计与架构、微服务架构可遵循 DDD 思想、原则与模式。
寄语
思考本质,理解领域驱动设计的原理,融会贯通地
运用设计原则解决问题,而非”规范“。思考限界上下文边界的划分,实际上还是“高内聚、低耦合”原则的体现,只是我们需要考虑什么内容才是高内聚的,如何抽象才能做到低耦合?
- 是否需要提取单独的限界上下文?是为了考虑职责的重用,还是为了它能够独立进化以应对未来的变化?
- 在分层架构中,各层之间该如何协作?如果出现了依赖,该如何解耦?仍然需要从重用与变化的角度去思考设计决策。
为什么同样遵循领域驱动设计,不同的系统会设计出不同的架构?这是因为不同的场景对架构质量的要求并不一样,我们要学会对架构的关注点做优先级排列,从而得出不同的架构决策。
Chap2 DDD 概览
DDD 将要解决的业务概念和业务规则转换为软件系统中的类型以及类型的属性与行为,通过合理运用面向对象的封装、继承和多态等设计要素,降低或隐藏整个系统的业务复杂性。
设计过程
一种思维方式,也是一组优先任务,它旨在加速那些必须处理复杂领域的软件项目的开发。
需求分析
- 建模
- 架构
- 设计
- 编码实现
- 对编码的测试与重构
DDD 强调领域模型的重要性,并通过模型驱动设计来保障领域模型与程序设计的一致。
- 从业务需求(问题域和业务期望)中提炼出统一语言(Ubiquitous Language),再基于统一语言建立领域模型;
- 这个领域模型会指导着程序设计以及编码实现;
- 最后,又通过重构来发现隐式概念,并运用设计模式改进设计与开发质量。
这个过程是一个覆盖软件全生命周期的设计闭环,每个环节的输出都可以作为下一个环节的输入,而在其中扮演重要指导作用的则是“领域模型”。
在为问题域寻求解决方案时,需要从宏观层次划分不同业务关注点的子领域,然后再深入到子领域中从微观层次对领域进行建模。
战略设计阶段
战略设计阶段是从下面两个方面来考量的:
- 问题域方面:针对问题域,引入限界上下文(Bounded Context)和上下文映射(Context Map)对问题域进行合理的分解,识别出核心领域(Core Domain)与子领域(SubDomain),并确定领域的边界以及它们之间的关系,维持模型的完整性。
- 架构方面:通过分层架构来隔离关注点,尤其是将领域实现独立出来,能够更利于领域模型的单一性与稳定性;引入六边形架构可以清晰地表达领域与技术基础设施的边界;CQRS 模式则分离了查询场景和命令场景,针对不同场景选择使用同步或异步操作,来提高架构的低延迟性与高并发能力。
战略设计的初衷是要保持模型的完整性。限界上下文的边界可以保护上下文内部和其他上下文之间的领域概念互不冲突。
将领域驱动设计的战略设计模式引入到架构过程中时,限界上下文不仅限于对领域模型的控制,还在于分离关注点之后,使得整个上下文可以成为独立部署的设计单元,这就是“微服务”的概念。上下文映射的诸多模式则对应了微服务之间的协作。
因此在战略设计阶段,微服务扩展了领域驱动设计的内容,反过来领域驱动设计又能够保证良好的微服务设计。
模型完整性:领域的边界和它们之间的关系。
类似于身体的完整性由器官和器官之间的关系组成。
边界给了实现限界上下文内部的最大自由度,这也是战略设计在分治上起到的效用,我们可以在不同的限界上下文选择不同的架构模式。
例如,针对订单的查询与处理,选择 CQRS 模式来分别处理同步与异步场景;还可以针对核心领域与子领域重要性的不同,分别选择领域模型(Domain Model)和事务脚本(Transaction Script)模式,灵活地平衡开发成本与开发质量。在宏观层面,面对整个软件系统,我们可以采用前后端分离与基于 REST 的微服务架构,保证系统具有一致的架构风格。
战术设计阶段
整个软件系统被分解为多个限界上下文(或领域)后,就可以分而治之,对每个限界上下文进行战术设计。领域驱动设计并不牵涉到技术层面的实现细节,在战术层面,它主要应对的是领域的复杂性。领域驱动设计用以表示模型的主要要素包括:
- 值对象(Value Object)
- 实体(Entity)
- 领域服务(Domain Service)
- 领域事件(Domain Event)
- 资源库(Repository)
- 工厂(Factory)
- 聚合(Aggregate)
- 应用服务(Application Service)
Eric Evans 通过下图勾勒出了战术设计诸要素之间的关系:
领域驱动设计围绕着领域模型进行设计,通过分层架构(Layered Architecture)将领域独立出来。表示领域模型的对象包括:实体、值对象和领域服务,领域逻辑都应该封装在这些对象中。这一严格的设计原则可以避免业务逻辑渗透到领域层之外,导致技术实现与业务逻辑的混淆。在领域驱动设计的演进中,又引入了领域事件来丰富领域模型。
聚合是一种边界,它可以封装一到多个实体与值对象,并维持该边界范围之内的业务完整性。在聚合中,至少包含一个实体,且只有实体才能作为聚合根(Aggregate Root)。注意,在领域驱动设计中,没有任何一个类是单独的聚合,因为聚合代表的是边界概念,而非领域概念。在极端情况下,一个聚合可能有且只有一个实体。
工厂和资源库都是对领域对象生命周期的管理。前者负责领域对象的创建,往往用于封装复杂或者可能变化的创建逻辑;后者则负责从存放资源的位置(数据库、内存或者其他 Web 资源)获取、添加、删除或者修改领域对象。领域模型中的资源库不应该暴露访问领域对象的技术实现细节。
演进的领域驱动设计过程
战略设计会控制和分解战术设计的边界与粒度,战术设计则以实证角度验证领域模型的有效性、完整性与一致性,进而以演进的方式对之前的战略设计阶段进行迭代,从而形成一种螺旋式上升的迭代设计过程,如下图所示:
面对客户的业务需求,由领域专家与开发团队展开充分的交流,经过需求分析与知识提炼,以获得清晰的问题域。通过对问题域进行分析和建模,识别限界上下文,利用它划分相对独立的领域,再通过上下文映射建立它们之间的关系,辅以分层架构与六边形架构划分系统的逻辑边界与物理边界,界定领域与技术之间的界限。之后,进入战术设计阶段,深入到限界上下文内对领域进行建模,并以领域模型指导程序设计与编码实现。若在实现过程中,发现领域模型存在重复、错位或缺失时,再进而对已有模型进行重构,甚至重新划分限界上下文。