文章目录
DDD和MVC的比较
MVC仅反映软件架构的分层,不定义业务语义的抽象和表达;使用用小规模业务架构;
DDD优点:
统一语言:同一种语言,易于理解,保证效率的最大化
清晰的边界定义:避免边界纷争和业务混乱,代码结构清晰
领域能力沉淀和复用:可复用性,是衡量软件架构设计优秀与否的一个重要指标,使业务和模型统一,也使领域能力得以传承
面向业务建模:聚焦于领域模型,而不是传统理念中关注数据层和接口层的设计,
设计和代码的等价:设计即代码,代码直观表现设计(在代码结构、层次、定义上会感受得更清楚)
对于个人:
提升全局视野:领域专家,也往往是半个业务专家
提升业务感:DDD是面向业务建模为出发点,所以一切都需要在具备优秀的业务敏感度下进行
构建体系化思维:不能只从单点去看问题,需要从线和面上去深入理解;体系化思维其实也是结构化思维的一种呈现,在这里推荐一本书《金字塔思维》
DDD缺点:
不适合逻辑相对简单的业务和产品:如小业务的CRUD
不适合非业务形态产品和应用:如bigdata
概要
3.范式
范式是指软件设计和开发的基本风格和哲学。它通常定义了编程的基本原则和模式。常见的软件设计范式包括:
结构化编程:强调程序结构的重要性,使用顺序、选择和循环控制结构
面向对象编程:基于对象的概念,将数据和处理数据的方法封装到一起
函数式编程:将计算视为函数的评估,避免状态改变和可变数据
事件驱动编程:以事件为中心,响应用户操作、消息或其他系统事件
- 模型
模型是对软件系统的抽象表示,用于帮助理解、设计和测试系统。常用的软件设计模型包括:
UML统一建模语言:一套图形化的建模语言,用于描述、设计和文档化软件项目
ER模型:用于数据库设计,描述数据的实体及其之间的关系
状态机模型:描述系统可能的状态、事件和这些事件发生时的转换
- 框架
框架是一套预先定制的代码和组件,用于提供软件开发的骨架。框架通常定义了应用程序的结构,提供了一组通用的功能和模式,以便开发者可以专注于实现特定的业务逻辑。
Spring Framework
Ruby on Rails:一个用于快速开发web应用程序的Ruby框架
Django:一个高级的Python Web框架
- 方法论
方法论是指一套指导软件开发过程的规则和实践。它包括项目管理、开发流程、团队协作等方面。常用的软件开发方法论如下:
敏捷开发:一种迭代和增量的开发方法,强调灵活性和客户合作
Scrum:一种敏捷开发框架,用于管理复杂的软件和产品开发
瀑布模型:一种线性顺序的开发方法,将项目分为不同阶段,每个阶段完成后进入下一个阶段
- 软件设计的主要活动
软件设计的主要活动包括:
建模:通过创建模型来表示系统的不同方面,如使用UML图来描述系统架构
测试:确保软件的质量,包括单元测试、集成测试、系统测试和验收测试
工程:应用工程原则和实践来构建软件,包括需求分析、设计、实现和测试
开发:编写代码和实现功能,将设计转换为实际的软件产品
部署:将软件发布到生产环境,使其可供用户使用
维护:对软件发布后对其进行更新和改进,修复缺陷,提升性能和适应性
每个活动都是软件开发生命周期的重要组成部分,它们相互依赖,共同确保软件项目的成功。
DDD核心理念:
- 统一语言(Ubiquitous Language):减少沟通成本和误解
- 限界上下文(Bounded Context):明确界定的系统边界
- 聚合(Aggregate):一组相关对象的集合,每个聚合都有一个聚合根,它是外部对象与聚合内部对象交互的唯一入口,体现封装特性
- 领域服务:当某些行为不自然属于任何实体或值对象时,这些行为可以被定义为领域服务,领域服务通常表示领域中一些操作或业务逻辑
- 应用服务:应用服务是软件的一部分,它们协调领域对象来执行任务。它们负责应用程序的工作流程,但不包含业务规则和知识。
- 基础设施:包括为领域模型提供持久化机制(如数据库)、消息传递、应用程序配置等技术组件
- 领域事件:领域事件是领域中发生的有意义的业务事件,它们可以触发其他子系统的反应和流程
领域
是指具体业务领域的知识、业务逻辑、数据以及业务规则的集合。它是软件要解决问题的业务环境,通常由一系列子领域组成,每个子领域代表一个业务中的一个特定的部分。
它包括以下特性:
- 业务中心:领域是围绕业务需求和业务规则构建的,它是软件设计的核心
- 模型驱动:领域模型是对业务知识的抽象,它通过领域实体、值对象、服务、聚合等概念来表示
- 语言一执性:领域模型的构建基于统一语言,这是开发团队和业务专家共同使用的语言,确保沟通无歧义
- 边界清晰:领域模型定义了清晰的边界,这些边界划分了不同的子领域和聚合,有助于管理复杂性和维护性
领域的用途主要包括下面三种:
- 业务逻辑和封装:领域模型封装了业务逻辑,使得业务规则和数据操作集中管理,便于理解和维护。
- 沟通工具:领域模型作为开发团队于业务专家之间的共同语言,有助于提高沟通效率,确保软件开发紧密跟随业务需求
- 软件设计的基础:领域模型是软件设计的基础,它指导着软件的架构和实现
领域模型的实现手段如下:
- 实体(Entity):具有唯一标识的领域对象,代表业务中的实体
- 值对象(Value Object):描述领域中的一些特性和概念,没有唯一标识,通常是不可变的
- 聚合(Aggregate):一组相关实体和值对象的集合,它们一起构成了一个数据和业务规则的单元
- 领域服务(Domain Service):在领域模型中执行特定业务逻辑的无状态服务,通常操作多个实体或聚合
- 领域事件(Domain Event):表示领域汇总发生的重要业务事件,用于解耦系统的不同部分
- 仓储(Repository):提供对聚合根的持久化操作,如保存和检索,通常于数据库交互
- 领域适配器(Domain Adapter):领域适配器是适配器模式在DDD的应用,他的目的是使的领域模型能够与外部系统或技术细节进行交互,而不受到污染
工厂(Factory):用于创建复杂的聚合或实体,封装创建逻辑。
通过上面介绍的手段,DDD使的软件设计更加贴近业务需求,提高了软件的质量和维护性。开发团队可以更好的理解业务逻辑,从而设计出更加健壮和灵活的系统。
战略方法论
领域(Domain
-
领域(Domain)是指业务问题的特定领域范围,它涉及到特定业务的规则、概念、业务流程等。领域是对业务问题进行分解和组织的基本单位
-
子域(Subdomain)是指在一个大的领域中划分出的相对独立的子领域,它通常代表一个独立的业务领域,具有特定的业务逻辑和功能需求。子域可以是整个系统的一个功能模块,也可以是一个独立的业务流程。
-
通用域(Generic Domain)是指与特定业务领域无关的通用功能,它是在整个领域中被多个子域所共享和复用的功能。通用域包括一些通用的服务、工具、组件等,用于支持多个子域的实现如config,缓存组件。
-
支撑域(Supporting Domain)指的是与核心业务的实现和发展密切相关的非业务功能。这些支撑域可以包括安全认证、用户管理、日志记录等在整个系统中被多个子域所共享和使用的基础设施功能。支撑域和通用域概念上有些类似,区分他们的标准简单归纳下的话,支撑域是由外域提供的能力,通用域是本域提供。
-
限界上下文(Bounded Context)则是DDD中的一个重要概念,用于划分和隔离不同的领域或子系统。一个限界上下文指的是一个明确定义了特定业务领域所围绕的边界,它包含了领域模型、业务规则、相关的领域服务和持久化逻辑等。不同的限界上下文之间可以是相互隔离的,每个上下文内部有其自己的语言和模型,但是它们之间也需要通过明确定义的接口进行通信。
小结
在DDD战略设计中,首先需要进行领域建模,即通过与领域专家的合作和深入领域知识的研究,将业务需求转化为领域模型。接下来,根据领域模型进行软件设计和开发,实现业务逻辑和功能。在整个过程中,需要保持与业务专家的紧密合作,不断迭代和验证领域模型的准确性和有效性
战术方法论
实体
代表领域中具有唯一身份和生命周期的对象。实体有自己的行为和状态,并通过标识属性进行唯一标识。
实体=唯一标识+状态属性+行为动作
实体具有以下特性:
- 唯一标识:实体具有一个可以区别其他实体的标识,这个标识符可以是一个ID、一个复合键或者是一个自然键,关键是它能唯一的标识实体实例
- 领域标识:实体的标识来源于业务领域,例如用户id、订单ID等,这些标识符在业务上有特定的含义,并且在系统中唯一
- 委派标识:在某些情况下,实体的标识可能是由ORM框架自动生成的,如数据库自增键,这种标识虽然可以唯一标识一个实体,但是它并不直接来源于业务领域
实现实体的手段如下:
- 定义实体类:在代码中定义一个类,该类包含实体的属性、构造函数和方法等
- 实现唯一标识:为实体类提供一个唯一标识的属性,如ID,并确保在实体的生命周期中这个标识保持不变
- 封装行为:在实体类中实现业务逻辑方法,这些方法主要是用于操作实体的状态,并执行相关的业务规则
- 使用ORM框架:利用ORM框架,将实体映射到数据库表中,这样可以简化数据持久化操作
- 实现领域服务:对于跨实体或跨聚合的操作,可以实现领域服务来处理这些操作,而不是在实体中直接实现
- 使用领域事件:当实体的状态发生变化时,可以发布领域事件,这样可以通知其他部分的系统进行相应的处理
领域模型:
- 贫血模型:贫血模型是指领域对象只具有数据属性,缺乏相关的行为逻辑。在贫血模型中,业务逻辑主要存在于服务层或者其它外部对象中,领域对象仅被视为被动的数据容器。这种模型在领域驱动设计(DDD)中被认为是反模式,因为它无法更好地体现领域的核心概念和规则。
- 失血模型:和贫血模型相似,指的是领域对象缺少业务逻辑和领域行为,以至于数据和行为的重要部分都被丢失。这种模型经常出现在面向对象的开发中,尤其是基于关系数据库的实现中。
- 充血模型:充血模型是在领域对象中充分体现了业务逻辑和行为的模型。充血模型积极地包含了数据和相关的行为逻辑,它使得领域对象能够更好地封装与之相关的业务规则和行为,提供了更加一致和抽象的编程接口。充血模型是DDD中推崇的设计模式,使得领域对象能够成为业务规则的中心。
- 涨血模型:涨血模型是指在充血模型的基础上,进一步将领域对象的状态和行为扩展到前沿技术和新的设计模式中(有些理念力,涨血模型和充血模型的区别在于,涨血模型添加了持久层的行为)。
充血模型虽然是DDD中推崇的设计模式,通过领域实体,一些关键行为和逻辑其实也能一起拿到了,但是在我的经验中,我更喜欢使用贫血+充血的混合模型(或者叫充血模型的简化版),因为这里涉及到一个标准的建立问题,如果只用充血模型的话,哪些行为和逻辑该下方到接口服务层,哪些又该收拢到实体中,这里面每个人的理念不一样。而我的标准是,涉及到持久层和复杂行为都下放到服务层,简单行为放到实体模型中。这样有个好处,随着业务的发展,如果只用充血模型,你的实体会越来越臃肿;如果只有贫血模型,自身又太单薄。所以一部分行为下放到服务层,我可以更细粒度的拆分服务接口,保证更优良的边界和代码可读性,同时也保证了模型自身的健壮性。
最后,不论哪种模型,都没有绝对的好坏,能够很好的定义出设计标准,同时基于自己的理解设计出符合业务扩展的实体和服务就是好的模型。
值对象
代表领域中没有唯一标识的对象,它的相等性是通过值的相等性来判断的,而不是通过标识。比如地址信息,手机号码,标签属性等。
聚合
一组相关对象的集合,由一个根实体(Aggregate Root)作为集合的入口点。聚合定义了一致性边界,通过聚合根管理其内部对象的生命周期和完整性。
聚合有一个聚合根和上下文边界,边界根据业务需求和内聚原则,定义了聚合应该包含哪些实体和值对象,而聚合之间是松耦合的,这样设计的微服务,会很自然地实现高内聚、低耦合。
聚合是由紧密关联的实体和值对象组成,是修改和保存数据的基本单位。每个聚合都有一个资源仓库,用于保存聚合的数据
电商中的订单聚合包括多个实体对象,如订单明细、支付信息、收货信息等
通过设计正确的聚合,订单系统的业务操作会非常清晰,并能够集中管理。
聚合根
聚合根是聚合的根实体,它是一组相关对象的入口点,管理着聚合内其他对象的生命周期和完整性。聚合根通过封装聚合内部的对象,并定义了聚合的一致性边界,确保聚合内的对象之间的关系和约束得到维护。外部对象只能通过聚合根来访问和操作聚合内的对象,从而保证了聚合的完整性和一致性。
可以把聚合根看作是部门的负责人,它不仅是实体,也是这个聚合“部门”的管理者。
聚合根作为实体时,有自己的属性和业务行为,执行自己的业务逻辑。
当聚合根作为管理者,它在聚合内部协调实体和值对象,按照固定的业务规则完成业务逻辑。
当聚合之间协作时,聚合根是对外的接口人。它通过自己的ID关联其他聚合,接受外部请求。如果需要访问聚合内的其他实体,必须先访问聚合根,然后导航到聚合的内部实体。外部对象不能直接访问聚合内实体。
订单聚合为例:必然以订单的单号为入口进行一系列的创建,查询,支付,发货关联信息等业务操作,而没有其他入口,这里订单就是订单聚合的聚合根
以下案例来自:架构师汤师爷 链接:https://www.zhihu.com/question/471813161/answer/3465876639
聚合的一些设计原则设计聚合时,遵循一些核心原则可以确保聚合的有效性、一致性。
- 选择合适的聚合根每个聚合都应有一个聚合根,它是聚合中最重要的实体,其他实体和值对象通过聚合根关联。聚合根应该是能够代表整个聚合的实体,它负责保证聚合内部数据的一致性和完整性。选择聚合根时,应考虑哪个实体在业务中扮演核心角色,并且能有效地管理和封装聚合的行为。
- 聚合最小化聚合应尽可能地小,只包含必须由聚合根直接管理的实体和值对象。这样可以降低聚合内部的复杂性,提高聚合的内聚性,小的聚合更容易理解和维护。
- 封装业务规则聚合根应该封装其聚合内部的业务规则,任何对聚合内部数据的修改,都应通过聚合根的方法进行访问,方法执行后应保证所有业务规则都被正确执行。通过这种方式,聚合根可以确保聚合的状态始终一致。
- 聚合间的引用通过标识符聚合之间不应直接引用对方的实体,而应该通过标识符(如ID)进行引用。这样做有助于减少聚合间的耦合,使得各个聚合可以独立地进行变更和扩展,而不会影响到其他聚合。
- 一致性边界的定义设计聚合时,要明确哪些操作必须是强一致的,即在完成操作后,聚合的状态必须是一致,这涉及到事务操作。聚合应该是最小的一致性边界,任何事务应当只在单个聚合的范围内完成,不应跨聚合操作。
下面列举一些常见的业务操作,介绍聚合是如何被使用的。
- 订单创建操作
当客户选完商品,并提交订单时,系统会触发订单创建的流程。
系统首先创建一个新的订单聚合实例,此实例以订单为聚合根。订单聚合根包含了必要的信息,如订单编号、订单初始状态等。
客户选定的每个商品都会作为订单明细,添加到订单中。每个订单明细实体包括商品ID、购买数量和单价等信息。这些订单明细在创建过程中由订单聚合根动态管理,确保数据的完整性。
客户提供的收货地址被创建为值对象,并与订单聚合关联。同时,初始化的支付信息也会被设置为一个实体,包括支付方式和支付状态等信息。
2.支付处理
当客户进行支付时,支付信息实体会被更新,包括记录支付金额、支付方式、支付时间等。订单聚合根确保支付信息与订单的状态保持一致性。
支付成功后,订单聚合根会将订单状态更新为“已支付”,这是通过聚合内部的业务逻辑完成,确保所有数据一致。
3.发货处理
在订单准备发货时,订单聚合根会验证存储的收货地址信息的完整性和准确性。如果地址不完整,可能会要求客户提供更多信息,或进行二次确认。
一旦发货地址验证无误,且商品准备就绪,订单聚合根将订单状态更新为“已发货”。随后,实际物流操作开始进行,并在系统中记录和跟踪发货的过程信息。
小结
聚合=服务模块(多个实体类集合,业务)
聚合根=service的主体,作为入口操作一些列内部实体
资源库Repository
资源库是用于管理领域对象的创建、更新和持久化的接口。它实现了将领域对象从内存中存储到持久化介质(如数据库)中,以及从持久化介质中检索对象并还原为领域对象的功能。资源库隐藏了底层的数据访问细节,提供了一致的接口和抽象,使得领域对象的访问和持久化变得简单和统一。
领域事件
代表领域中的发生的重要事件,可以用于通知其他领域对象或跨限界上下文进行触发异步消息实现解耦和协作。
实现方式如下:
领域层
定义事件接口:创建一个或多个接口来定义领域事件的结构和行为
创建领域事件类:基于定义的接口,实现具体的领域事件类,包含必要的属性和方法
触发领域事件:在离领域逻辑中的适当位置,实例化并发布领域事件
基础层
实现领域接口:使用消息队列来实现领域事件发布和订阅
触发器层/接口层
监听领域事件消息:在系统的其他部分或外部系统中,监听领域事件并根据事件来执行相应的业务逻辑或集成逻辑
领域服务
在DDD的上下文中,领域服务是一种封装了特定领域操作的服务。它是实现领域模型中的业务逻辑的一种手段,特别是当这些逻辑不属于任何一个实体或值对象时。领域服务通常用于是心啊跨越多个实体或值对象的行为,或者是那些不适合放在单个实体中的操作。
它具有以下特性:
领域逻辑的封装 :领域服务封装了领域特定的业务逻辑,这些逻辑通常涉及多个领域对象的交互,这种封装有助于保持实体和值对象的职责单一和清晰
无状态:领域服务通常是无状态的,它们不保存任何业务数据,而是操作领域对象来完成业务逻辑。这有助于保持服务的可重用性和可测试性。
独立性:领域服务通常与特定的实体或值对象无关,它们提供了一种独立于领域模型的其他部分的方式来实现业务规则
重用性:领域服务可以被不同的应用请求重用,例如不同的应用服务编排或领域事件处理器
接口清晰:领域服务的接口应该清晰的反映其提供的业务能力,参数的返回值应该是领域对象或基本数据类型
实现方式如下:
设计原则和模式
通过使用设计原则(如单一职责原则、开闭原则)和设计模式(工厂、策略、模版、组合、责任链)对功能逻辑进行解耦,可以提高领域服务的灵活性和可维护性
功能拆分
不应该只定义一个service接口,然后在实现类下编写所有的逻辑,相反,应该对功能进行子包的拆分,以保持领域服务的职责清晰和管理易于维护
依赖抽象
领域服务应该依赖于抽象而不是具体的实现,这意味着领域服务应该通过接口或外部资源(如数据库、外部API)交互,而不是依赖于具体的实现。这样可以提高领域服务的可测试性和灵活性
协作和编排
领域服务可能需要与其他的领域服务协作以完成复杂的业务操作,在这种情况下,应该设计清晰的协作和编排机制,以确保业务逻辑的正确性和一致性。
小结
DDD战术设计是在DDD战略设计的基础上,着重于解决如何将业务需求和设计模型有效地映射和实现的具体方法和技术。它的目标是根据战略设计的指导,通过合理的领域建模和架构设计,将领域问题转化为高内聚、低耦合的代码实现,从而更好地满足业务需求。
领域建模
领域建模,它的目的归纳起来就一句话:提炼业务知识,形成统一语言,沉淀领域模型。
领域建模的优秀与否,可以说直接决定着本次设计的成败,因为一旦发生建模边界不清晰,实体划分错乱,核心属性没有遵守开闭原则等问题,虽然当下可以正常交付业务,但是对于整个项目的后续发展可以说是灾难性的
领域模型:包含领域对象、属性、关系、行为、边界范围等各个方面,用于描述业务的本质,这也是最重要的产出物。
用例图:用于明确系统的功能。
数据模型:描述系统的数据结构和关系,包括实体关系模型、关系数据库模型等。
状态图:用于描述系统各个状态及其转移条件。
活动图:用于描述系统流程中的各个活动及其关系。
序列图:描述系统中各个对象之间的交互过程和消息传递序列。
架构模型:包含系统的物理和逻辑结构,包括组件、模块、接口等。
事件风暴建模
事件风暴(Event Storming)是一种用于快速探索、理解和设计领域模型的合作工作坊技术,需要配合建模方法(如领域驱动设计)和工具(如UML、流程图等)来进一步详细和完善领域模型形成建模文档
通过团队协作的方式,以用户的视角来讨论和探索整个业务流程。参与者将自己的理解和知识通过贴在墙上的便利贴上表达出来,核心会围绕着事件去编排整个业务流程。事件可以是任何对业务、系统或用户有意义的事情,包括用户触发的操作、系统的状态转换、通信和消息传递等。这些事件以一种自顶向下的方式,以时间线的形式贴在墙上。随着讨论的深入,团队可以探索和辨识出各种概念、实体、聚合根、资源库、上下文边界、业务流程和事件的关联关系。这有助于更全面地理解整个领域的复杂性,并为后续的领域建模和业务流程设计提供线索和洞察。
事件风暴具有高度可视化的特点,能够促进团队之间的沟通和共享知识。它也可以帮助团队快速理解现有系统的复杂性,并为系统的重构和演进提供指导。此外,事件风暴还可以用作需求分析、业务流程优化和团队协作的工具。
需要注意的是,事件风暴并非一种正式的建模方法,而是一种协作工作坊技术。在事件风暴中,没有确定的固定语法,但是有一些常用的技术和简写符号,用于记录和表示不同的业务事件和概念
事件风暴是一个比较“重”的方法,对于一些0-1建设的大型项目(1000人日以上)比较适合,一些中小型项目有些“过渡设计”
事件风暴的语法并不是严格规定的,而且可以根据团队的需要和偏好进行适当的调整。重点是通过快速的头脑风暴,来协作识别和探索业务领域的关键事件,以促进团队的共享理解和协作。
事件风暴的本质上是通过脑暴的方式,围绕关键领域事件串联整个业务场景的生命周期,通过发散去收集,通过收敛去提炼,要真正把这个方法用好,有两个关键点:
主持人的综合能力,尤其体系在最后的收敛上,是否能把如此多的脑暴信息,提炼成关键点。
事件风暴语法和符号:
用户角色:通常使用人物标签或者角色名字来代表具体的用户,例如 “客户”、“管理员”等。
业务事件:使用动词来描述业务活动或事件,如“创建订单”、“审核申请”、“发货”等。
识别的领域概念:在事件风暴中,通过写在有色便利贴上来标记关键的业务概念,例如“订单”、“产品”、“支付”等。这些概念有助于团队识别和理解业务的重要方面。
粘贴便利贴:使用不同颜色的便利贴来表示不同的类型或者关注点。例如,可以使用黄色便利贴表示业务活动和概念,使用蓝色便利贴表示抽象的过程和规则,使用粉色便利贴表示意见、问题或待解决的事项。
关系箭头:可以使用箭头来表示业务事件之间的关系,例如表示事件的先后顺序、依赖关系等。
四色建模
Peter Coad在他的书《Java Modeling In Color With UML》中,提出了一种与颜色相关的建模方法,被称为Color Modeling。这种方法使用颜色作为一种可视化技巧,用于在领域模型中表示不同的对象和概念。它的目的是通过使用颜色来帮助开发者更清晰地理解和传达模型的结构和关系。
四色建模并非DDD的核心概念或原则,它更多地是作为一种模型建立和可视化的辅助工具。
DDD更关注于如何基于通用语言、领域模型和限界上下文等概念进行软件系统的设计与开发。
四色模型的语法:
时标原型(Moment-Interval Archetype,也称业务关键时刻,简称MI):表示事物在某个时刻或某一段时间内发生的,如销售订单、收款记录等,使用浅红色表示。
PPT原型(Part-Place-Thing Archetype,人/事/物原型,简称PPT):表示参与扮演不同角色的人或事物,如商品、账户、店铺等,使用浅绿色表示。
角色原型(Role Archetype,简称ROLE):抽象了一种参与方式,由人或组织机构、地点或物品来承担,如客户、商家、财务组织等,使用浅黄色表示。
描述原型(Description Archetype,简称DESC):对上述颜色表示的内容进行解释,用于分类或者描述建模过程中产生的数据、事件或者活动,使用浅蓝色表示。
用一句话来概括四色原型就是:一个什么样的人或物品以某种角色在某个时刻或某段时间内参与某个活动。
参考
一文搞懂DDD的12个核心概念与2大建模方法:https://new.qq.com/rain/a/20240523A04JCJ00?suid=&media_id=
DDD架构理论详解: https://blog.csdn.net/qq_43456605/article/details/138518119