聊聊领域驱动设计

背景前提

作为一个后端研发人员,领域驱动设计(DDD)这个概念你应该在工作经常听到,并且可能借用过它的思想来划分微服务边界、定义工程模块结构、设计编码风格。虽然大家在研发的过程中都或多或少开始用起来,但是少有人能说得清楚为啥要用,不用它就不行吗?它到底给我们带来了什么价值?

我们团队在设计和编写代码时也经常会参考DDD的思路,但是没有人真正去系统性的理解透彻,在遇到更抽象的领域问题,也就经常容易迷失方向。我们团队遇到的问题,就是在与外部团队协作时,经常出现拆皮的现象,最终争论半天的焦点都是在领域边界的划分上。

而DDD就是用来解决领域边界问题的,为什么到我们这就拆不清,道不明呢?大家怎么就没有用DDD来解决问题呢?

领域驱动设计探究

定义

2004年埃里克.埃文斯发表了《领域驱动设计》这本书,从此领域驱动设计(Domain Driver Design,简称DDD)诞生。DDD的核心思想是通过领域设计方法定义领域模型,从而确定业务和应用边界,保证业务模型和代码模型的一致性。

但 DDD 提出后在软件开发领域一直都是"雷声大,雨点小"!直到 Martin Fowler 提出微服务架构,DDD 才真正迎来了自己的时代。

DDD 是一种处理高度复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题。DDD 不是架构,而是一种架构设计方法论,它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现架构演进。

DDD 包括战略设计和战术设计两部分。

  • 战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。
  • 战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

问题解决方式

站在一个开发人员的角度,从技术视角出发,运用DDD解决的都是战术设计问题,更侧重于领域划分好之后的实现和落地。再深入一点,就是在一个领域内,把领域内部的模块边界定义清晰,解决一个领域内的事情。

而上面的问题是,两个不同团队之间的领域边界划分,这个是战略问题。不同的团队代表着公司不同的组织结构,也代表着不同的业务领域方向。当一个新的功能出现,都不属于两个团队现有的领域范畴,那一定是团队的领域定义本身出了问题,需要重新来划分不同团队的领域边界。这个时候,研发人员基于战术视角是解决不了这种战略设计的问题。那么DDD是如何进行战略设计的呢?

DDD 战略设计会建立领域模型,领域模型可以用于指导微服务的设计和拆分。事件风暴是建立领域模型的主要方法,它是一个从发散到收敛的过程。它通常采用用例分析、场景分析和用户旅程分析,尽可能全面不遗漏地分解业务领域,并梳理领域对象之间的关系,这是一个发散的过程。事件风暴过程会产生很多的实体、命令、事件等领域对象,我们将这些领域对象从不同的维度进行聚类,形成聚合、限界上下文等边界,从而建立领域模型,这就是一个收敛的过程。

一般情况下,我们运用三步就可以划定领域模型,确定微服务的边界。

  • 在事件风暴中梳理业务过程中的用户操作、事件以及外部依赖关系等,根据这些要素梳理出领域实体等领域对象。
  • 根据领域实体之间的业务关联性,将业务紧密相关的实体进行组合形成聚合,同时确定聚合中的聚合根、值对象和实体。在这个图里,聚合之间的边界是第一层边界,它们在同一个微服务实例中运行,这个边界是逻辑边界,所以用虚线表示。
  • 根据业务及语义边界等因素,将一个或者多个聚合划定在一个限界上下文内,形成领域模型。在这个图里,限界上下文之间的边界是第二层边界,这一层边界可能就是未来微服务的边界,不同限界上下文内的领域逻辑被隔离在不同的微服务实例中运行,物理上相互隔离,所以是物理边界,边界之间用实线来表示。

在战略设计中我们建立了领域模型,划定了业务领域的边界,建立了通用语言和限界上下文,确定了领域模型中各个领域对象的关系。到这儿,业务端领域模型的设计工作基本就完成了,这个过程同时也基本确定了应用端的微服务边界。

总结

在从业务模型向微服务落地的过程中,也就是从战略设计向战术设计的实施过程中,我们会将领域模型中的领域对象与代码模型中的代码对象建立映射关系,将业务架构和系统架构进行绑定。当我们去响应业务变化调整业务架构和领域模型时,系统架构也会同时发生调整,并同步建立新的映射关系。

所以当我们的团队跟外部团队合作,基于领域的边界产生争论无法达成一致时。较好的方式是,暂时搁置争议,回去各自带领团队运用事件风暴重新梳理团队的领域模型和领域边界,再请公司的架构师或者部门负责人来一起进行商议划分的合理性。

相关推荐
编程洪同学22 分钟前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
-$_$-32 分钟前
【LeetCode 面试经典150题】详细题解之滑动窗口篇
算法·leetcode·面试
GraduationDesign1 小时前
基于SpringBoot的蜗牛兼职网的设计与实现
java·spring boot·后端
颜淡慕潇1 小时前
【K8S问题系列 | 20 】K8S如何删除异常对象(Pod、Namespace、PV、PVC)
后端·云原生·容器·kubernetes
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS安康旅游网站(JAVA毕业设计)
java·vue.js·spring boot·后端·kafka·开源·旅游
DogDaoDao2 小时前
leetcode 面试经典 150 题:矩阵置零
数据结构·c++·leetcode·面试·矩阵·二维数组·矩阵置零
搬码后生仔2 小时前
将 ASP.NET Core 应用程序的日志保存到 D 盘的文件中 (如 Serilog)
后端·asp.net
Suwg2092 小时前
《手写Mybatis渐进式源码实践》实践笔记(第七章 SQL执行器的创建和使用)
java·数据库·笔记·后端·sql·mybatis·模板方法模式
凡人的AI工具箱3 小时前
每天40分玩转Django:Django文件上传
开发语言·数据库·后端·python·django
spcodhu4 小时前
在 Ubuntu 上搭建 MinIO 服务器
linux·后端·minio