/ScaffoldingDDD

Scaffolding Engineering based on DDD thinking and spring Framework

Primary LanguageJavaGNU General Public License v3.0GPL-3.0

ScaffoldingDDD

Scaffolding Engineering based on DDD thinking and spring Framework
基于Spring框架 搭建DDD 领域驱动设计分层架构的脚手架WEB工程.

包结构

  • common 公共模块, 后面有可能抽取到独立的项目中,供各个微应用复用
  • domain 领域模型
  • adapter 应用适配器, 定义与外部系统交互的各类方式的接口, 包含相互调用
  • infrastructure 基础设施
  • infrastructure.adapter 基础设施适配器, 定义与外部系统交互的各类方式的接口, 包含相互调用
  • application 应用层, 定义应用程序提供的功能(用例)
  • test 测试

项目中包含的例子业务功能

用户登录 用户注册 用户角色管理 用户权限管理 用户角色权限管理 用户角色权限分配 用户角色

安装

gradle build

运行

gradle bootRun

访问

浏览器访问 http://localhost:8080/welcome

1.1. 前言 DDD这个**呢,最早是Eric Evans(埃里克·埃文斯)在2003年《Domain-Driven Design –Tackling Complexity in the Heart of Software》书中提出的一个概念,该书翻译过来就是领域驱动设计—软件核心复杂性应对之道,但是提出的时候微服务当时并没有流行,所以一直没有火起来,DDD最近开始流行的原因,主要是借着微服务的东风。 1.2. MVC模式 VS DDD模式 MVC三层开发模式大家应该都非常熟悉,现在公司开发基本都是这种模式。 image MVC开发流程:

用户需求转化为产品需求 需求评审会pm讲解需求转化为研发需求 研发人员根据需求进行设计库表结构 编写dao层代码 编写service代码 编写controller代码 这一步步下来,是不是感觉非常的丝滑。比如产品提了一个需求,首先我们一般会想考虑设计几张表,怎么存储数据,然后建立dao层,service层,controller层来实现这个功能。但是严格来讲,mvc本质上是一种面向数据的设计,主要关注数据,自低向上的**。虽然在开发速度上有一定优势,如果只追求开发速度,面向数据模型编程在短期之内可以搞定需求,但一味追求速度,如果你系统的业务变化快速,从长远来看随着时间的增长,系统堆了杂七杂八以后,MVC的短板就会日益明显。

1.2.1. MVC存在的问题: 1.新需求的开发会越来越难。 2.代码维护越来越难,一个类代码太多,这怎么看对吧,就是一堆屎山。 3.技术创新越来越难,代码没时间重构,越拖越烂。 4.测试越来越难,没办法单元测试,一个小需求又要回归测试,太累。

1.2.2. 使用DDD的意义: 单体架构局部业务膨胀可以拆分成微服务,微服务架构局部业务膨胀,又拆成什么呢? DDD就是为了解决这些问题的存在,从一个软件系统的长期价值来看,就需要用DDD,虽然一开始从设计到开发需要成本,但是随着时间的增长,N年以后代码依然很整洁,利于扩展和维护,高度自治,高度内聚,边界领域划分的很清楚。当然了,针对于简单的系统用DDD反而用复杂了,杀鸡焉用宰牛刀! MVC的开发模式:是数据驱动,自低向上的**,关注数据。 DDD的开发模式:是领域驱动,自顶向下,关注业务活动。 2、DDD领域驱动模型 2.1. 概念 不想看以下抽象概念的读者们,DDD有关demo的完整代码已经给大家整理好了,可以关注【微信公众号】微信搜索【老板来一杯java】,然后【加群】直接获取【DDD源码】,在自己项目中引入即用!

  1. DDD: DDD(Domain Driven Design)领域驱动模型,是一种处理高度复杂领域的设计**,不是一种架构,而是一种架构设计方法论,是一种设计模式。说白了就是把一个复杂的软件应用系统的其中各个部分进行一个很好的拆解和封装,以达到高内聚低耦合的这样一个效果。

说白了就是,DDD就是以高内聚低耦合为目的,对软件系统进行模块化的一种**。 2. 战略设计: 指的是领域名词、动词分析、提取领域模型。官方解释,在某个领域,核心围绕上下文的设计,主要关注上下文的划分、上下文映射的设计,通用语言的设计。

说白了就是,在某个系统,核心围绕子系统的设计;主要关注,这些子系统的划分,子系统的交互方式,还有子系统的核心术语的定义。

  1. 战术设计: 用领域模型指导设计及编码的实现。官方解释,核心关注上下文中的实体建模,定义值对象,实体等,更偏向开发细节。

说白了就是,上下文对应的就是某一个子系统,子系统里代码实现怎么设计,就是战术设计要解决的问题。核心关注某个子系统的代码实现,以面向对象的思维设计类的属性和方法,和设计类图没有什么区别,只是有一些规则而已。就是 指导我们划分类。

  1. 问题空间: 问题空间属于需求分析阶段,重点是明确这个系统要解决什么问题,能够提供什么价值,也就是关注系统的What与Why。 问题空间将问题域提炼成更多可管理的子域,是真对于问题域而言的。问题空间 在研究和解决业务问题时,DDD 会按照一定的规则将业务领域进行细分,当领域细分到一定的程度后,DDD 会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现该领域模型,解决相应的业务问题。简言之,DDD 的领域就是这个边界内要解决的业务问题域。

领域可以进一步划分为子领域。我们把划分出来的多个子领域称为子域,每个子域对应一个更小的问题域或更小的业务范围,每个子域又包含了核心子域,支撑子域,通用子域。

领域的核心**就是将问题域逐级细分,来降低业务理解和系统实现的复杂度。通过领域细分,逐步缩小服务需要解决的问题域,构建合适的领域模型。

说白了就是,就是系统下面有多个子系统,就是分了一些类型,比如电商系统,订单就是核心领域,支付调用银行,支付宝什么的就是支撑子域,相当于我们俗称的下游,通用子域,就是一些鉴权,用户中心,每个系统都会用到,就设计成通用子域,关键就是讨论过程如何得出这些问题域,是战略设计要解决的。

  1. 解决空间: 解决方案域属于系统设计阶段,针对识别出来的问题域,寻求合理的解决方案,也就是关注系统的How。在领域驱动设计中,核心领域(Core Domain)与子领域(Sub Domain)属于问题域的范畴,限界上下文(Bounded Context)则属于解决方案域的范畴。 说白了就是,得出这些问题域之后,就基于这些问题域来求解,属于解决空间。相当于,知道了y=2x,知道了x是多少,然后求y的值。解决空间就是指,领域之间的关系是什么样子,每个领域中通用的术语 ,具体在领域内怎么实现代码,进行领域建模就可以了。 从问题域到解决方案域,实际上就是从需求分析到设计的过程,也是我们逐步识别限界上下文的过程。

  2. 事件风暴: 事件风暴的基本**,就是将软件开发人员和领域专家聚集在一起,完成领域模型设计(领域分析和领域建模)。划分出微服务逻辑边界和物理边界,定义领域模型中的领域对象,指导微服务设计和开发。

领域分析,是根据需求划分出初步的领域和限界上下文,以及上下文之间的关系;然后分析每个上下文内部,抽取每个子域的领域概念,识别出哪些是实体,哪些是值对象;

领域建模,就是对实体、值对象进行关联和聚合,划分出聚合的范畴和聚合根;

DDD需要进行领域分析和领域建模,除了事件风暴之外实现的方法有,领域故事讲述,四色建模法,用例法等。

事件风暴是建立领域模型的主要方法,但是在 DDD 领域建模和系统建设过程中,有很多的参与者,包括领域专家、产品经理、项目经理、架构师、开发经理和测试经理等。对同样的领域知识,不同的参与角色可能会有不同的理解,那大家交流起来就会有障碍,怎么办呢?因此,在 DDD 中就出现了“通用语言”和“限界上下文”这两个重要的概念。

  1. 通用语言: DDD的主要参与者:领域专家+开发人员。领域专家擅长某个领域的知识,专注于交付的业务价值。而开发人员则注重于技术实现,总是想着类、接口、方法、设计模式、架构等。这也就导致了团队交流的困难性。因此找到双方的通用语言是解决该问题的有效途径。

通用语言定义上下文含义。在事件风暴过程中,通过团队交流达成共识的,能够简单、清晰、准确描述业务涵义和规则的语言就是通用语言。 通用语言包含术语和用例场景,并且能够直接反映在代码中。通用语言中的名词可以给领域对象命名,如商品、订单等,对应实体对象;而动词则表示一个动作或事件,如商品已下单、订单已付款等,对应领域事件或者命令。

通用语言说白了就是,使用团队(业务人员、产品经理、UI设计、前端设计、后端技术等)中大家都懂的概念,统一语言用可以用于需求文档、PRD文档、代码以及日常的沟通,可以解决交流障碍的问题,使领域专家和开发人员能够协同合作,从而能够确保业务需求的正确表达。

  1. 限界上下文: 官方解释:限界上下文主要用来封装通用语言和领域对象。 限界上下文可以拆分为两个词,限界和上下文。

限界:适用的对象一般是抽象事物,指不同事物的分界,指定某些事物的范围。

上下文:个人理解就是语境。语言都有它的语义环境,同样,通用语言也有它的上下文环境。为了避免同样的概念或语义在不同的上下文环境中产生歧义,DDD 在战略设计上提出了“限界上下文”这个概念,用来确定语义所在的领域边界。限界上下文就是用来定义领域边界,以确保每个上下文含义在它特定的边界内都具有唯一的含义,领域模型则存在于这个边界之内。这个边界定义了模型的适用范围,使团队所有成员能够明确地知道什么应该在模型中实现,什么不应该在模型中实现。 ​ 比如说,商品在不同的阶段有不同的术语,在销售阶段是商品,而在运输阶段则变成了货物。同样的一个东西,由于业务领域的不同,赋予了这些术语不同的涵义和职责边界,这个边界就可能会成为未来微服务设计的边界。那么,领域边界就是通过限界上下文来定义的。

限界上下文是微服务设计和拆分的主要依据。在领域模型中,如果不考虑技术异构、团队沟通等其它外部因素,一个限界上下文理论上就可以设计为一个微服务。

限界上下文是业务概念的边界,是业务问题最小粒度的划分。在某个业务领域中会包含多个限界上下文,我们通过找出这些确定的限界上下文对系统进行解耦,要求每一个限界上下文其内部必须是紧密组织的、职责明确的、具有较高的内聚性。说白了就是,上下文对应的就是某一个子系统,系统之间要划分好边界。

  1. 上下文映射: 上下文之间交互方式就是上下文映射,相对于系统里面这就是RPC,http等交互方式。

  2. 领域: 从广义上讲,领域具体指一种特定的范围或区域。在DDD中上下文的划分完的东西叫作领域,领域下面又划分了,核心领域,支撑子域,通用子域。 子域:在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。

核心域:它是业务成功的主要因素和公司的核心竞争力 通用域:没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域 支撑域:有一种功能子域是必需的,但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,就是支撑域。

说白了就是,系统下面有多个子系统,就是分了一些类型,比如电商系统,订单就是核心领域,支付调用银行,支付宝什么的就是支撑子域,相当于我们俗称的下游,通用子域,就是一些鉴权,用户中心,每个系统都会用到,就设计成通用子域,关键就是讨论过程如何得出这些域,是战略设计要解决的。

  1. 领域模型: 领域模型是对领域内的概念类或现实世界中对象的可视化表示。它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。 是描述业务用例实现的对象模型。它是对业务角色和业务实体之间应该如何联系和协作以执行业务的一种抽象。 领域模型分为领域对象和领域服务两大类,领域对象用于存储状态,领域服务用于改变领域对象的状态。

特点:

领域模型是对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质;领域模型是有边界的,只反应了我们在领域内所关注的部分; 领域模型只反映业务,和任何技术实现无关;领域模型不仅能反映领域中的一些实体概念,如货物,书本,应聘记录,地址,等;还能反映领域中的一些过程概念,如资金转账,等; 领域模型确保了我们的软件的业务逻辑都在一个模型中,都在一个地方;这样对提高软件的可维护性,业务可理解性以及可重用性方面都有很好的帮助; 领域模型能够帮助开发人员相对平滑地将领域知识转化为软件构造; 领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求; 要建立正确的领域模型并不简单,需要领域专家、设计、开发人员积极沟通共同努力,然后才能使大家对领域的认识不断深入,从而不断细化和完善领域模型; 为了让领域模型看的见,我们需要用一些方法来表示它;图是表达领域模型最常用的方式,但不是唯一的表达方式,代码或文字描述也能表达领域模型; 领域模型是整个软件的核心,是软件中最有价值和最具竞争力的部分;设计足够精良且符合业务需求的领域模型能够更快速的响应需求变化; 12. 领域事件: 聚合之间产生的业务协同使用领域事件的方式来完成,领域事件就是将上游聚合处理完成这个动作通过事件的方式进行抽象。

在DDD中有一个原则,一个业务用例对应一个事务,一个事务对应一个聚合根,也就是在一次事务中只能对一个聚合根操作。但在实际应用中,一个业务用例往往需要修改多个聚合根,而不同的聚合根可能在不同的限界上下文中,引入领域事件即不破坏DDD的一个事务只修改一个聚合根的原则,也能实现限界上下文之间的解耦。对于领域事件发布,在领域服务发布,在不使用领域服务的情况下,则由应用层在调用资源库持久化聚合根之后再发布领域事件。

一个事件可能当前限界上下文内也需要消费,即可能有多个限界上下文需要消费,一个事件对应多个消费者。

一个完整的领域事件 = 事件发布 + 事件存储 + 事件分发 + 事件处理。 事件发布:构建一个事件,需要唯一标识,然后发布; 事件存储:发布事件前需要存储,因为接收后的事建也会存储,可用于重试或对账等;就是每次执行一次具体的操作时,把行为记录下来,执行持久化。 事件分发:服务内的应用服务或者领域服务直接发布给订阅者,服务外需要借助消息中间件,比如Kafka,RabbitMQ等,支持同步或者异步。 事件处理:先将事件存储,然后再处理。 当然了,实际开发中事件存储和事件处理不是必须的。