slightlee / ddd-demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

基础概念

img_011

1、用户接口层

用户接口层负责向用户显示信息和解释用户指令。这里的用户可能是:用户、程序、自动化测试和批处理脚本等等。

2、应用层

应用层是很薄的一层,理论上不应该有业务规则或逻辑,主要面向用例和流程相关的操作。但应用层又位于领域层之上,因为领域层包含多个聚合,所以它可以协调多个聚合的服务和领域对象完成服务编排和组合,协作完成业务操作。

应用层也是微服务之间交互的通道,它可以调用其它微服务的应用服务,完成微服务之间的服务组合和编排。

应用服务

一般命名为*ApplicationService

它负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装,以粗粒度的服务通过 API 网关向前端发布。还有,应用服务还可以进行安全认证、权限校验、事务控制、发送或订阅领域事件等。

3、领域层

领域层的作用是实现企业核心业务逻辑,通过各种校验手段保证业务的正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。领域层包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。

领域服务

可以组合聚合内的多个实体(或者值对象),实现复杂的业务逻辑。

4、基础层

基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化。

基础层包含基础服务,它采用依赖倒置设计,封装基础资源服务,实现应用层、领域层与基础层的解耦,降低外部资源变化对应用的影响。

常见的架构模型

整洁架构(洋葱架构)

img_012

六边形架构(端口适配器架构)

img_013

三种架构的共同点

DDD 分层架构、整洁架构、六边形架构都是以领域模型 为核心,实行分层架构,内部核心业务逻辑与外部应用、资源隔离并解耦。

img_014

目录结构

├───application
│   ├───event
│   │   ├───publish
│   │   └───subscribe
│   └───service
├───domain
│   ├───aggregate
│   │   ├───entity
│   │   ├───event
│   │   ├───repository
│   │   └───service
│   └───aggregate2
├───infrastructure
│   ├───config
│   └───util
└───interfaces
    ├───assembler
    ├───dto
    └───facade

Interfaces(用户接口层)

它主要存放用户接口层与前端交互、展现数据相关的代码。前端应用通过这一层的接口,向应用服务获取展现所需的数据。 这一层主要用来处理用户发送的 Restful 请求,解析用户输入的配置文件,并将数据传递给 Application 层。数据的组 装、数据传输格式以及 Facade 接口等代码都会放在这一层目录里。

Assembler

实现 DTO 与领域对象之间的相互转换和数据交换。一般来说 Assembler 与 DTO 总是一同出现。

Dto

它是数据传输的载体,内部不存在任何业务逻辑,我们可以通过 DTO 把内部的领域 对象与外界隔离。

Facade

提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。

Application(应用层)

它主要存放应用层服务组合和编排相关的代码。应用服务向下基于微服务内的领域服务或外部微服务的应用服务完成服务的编排和组合,向上为用户接口层 提供各种应用数据展现支持服务。应用服务和事件等代码会放在这一层目录里。

Event(事件)

这层目录主要存放事件相关的代码。它包括两个子目录:publishsubscribe。前者主要存放事件发布相关代码,后者主要存放事件订阅相关代码(事件处理 相关的核心业务逻辑在领域层实现)。

Service(应用服务)

这层的服务是应用服务。应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是 一段独立的业务逻辑。你可以将所有应用服务放在一个应用服务类里,也可以把一个应用服 务设计为一个应用服务类,以防应用服务类代码量过大。

Domain(领域层)

它主要存放领域层核心业务逻辑相关的代码。领域层可以包含多个聚合代码包,它们共同实现领域模型的核心业务逻辑。聚合以及聚合内的实体、方法、领域服 务和事件等代码会放在这一层目录里。

Aggregate(聚合)

它是聚合软件包的根目录,可以根据实际项目的聚合名称命名,比如权限聚合。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内 实现高内聚的业务逻辑,它的代码可以独立拆分为微服务。

Entity(实体)

它存放聚合根、实体、值对象以及工厂模式(Factory)相关代码。实体类采用充血模型,同一实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码 在领域服务中实现。

Event(事件)

它存放事件实体以及与事件活动相关的业务逻辑代码。

Service(领域服务)

它存放领域服务代码。一个领域服务是多个实体组合出来的一段业务逻辑。你可以将聚合内所有领域服务都放在一个领域服务类中,你也可以把每一个领域服 务设计为一个类。如果领域服务内的业务逻辑相对复杂,我建议你将一个领域服务设计为一个领域服务类,避免由于所有领域服务代码都放在一个领域服务类中,而出现代码臃肿的问 题。领域服务封装多个实体或方法后向上层提供应用服务调用。

Repository(仓储)

它存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定了一个原则:一个聚合对应一个 仓储。

Infrastructure(基础层)

它主要存放基础资源服务相关的代码,为其它各层提供的通用 技术能力、三方软件包、数据库服务、配置和基础资源服务的代码都会放在这一层目录里。

Config

主要存放配置相关代码

Util

主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、 通用算法等基础代码,你可以为不同的资源类别建立不同的子目录。

数据对象视图

img_01

数据持久化对象 PO(Persistent Object)

与数据库结构一一映射,是数据持久化过程中的数据载体。

基础层

基础层的主要对象是 PO 对象。我们需要先建立 DO 和 PO 的映射关系。当 DO 数据需要持久化时,仓储服务会将 DO 转换为 PO 对象,完成数据库持久化操作。当 DO 数据需要初始化时,仓储服务从数据库获取数据形成 PO 对象,并将 PO 转换为 DO,完成数据初始化。

领域对象 DO(Domain Object)

微服务运行时的实体,是核心业务的载体。

领域层

领域层的主要对象是 DO 对象。DO 是实体和值对象的数据和业务行为载体,承载着基础的核心业务逻辑。通过 DO 和 PO 转换,我们可以完成数据持久化和初始化。

应用层

应用层的主要对象是 DO 对象。如果需要调用其它微服务的应用服务,DO 会转换为DTO,完成跨微服务的数据组装和传输。用户接口层先完成 DTO 到 DO 的转换,然后应用服务接收 DO 进行业务处理。如果 DTO 与 DO 是一对多的关系,这时就需要进行DO数据重组。

数据传输对象 DTO(Data Transfer Object)

用于前端与应用层或者微服务之间的数据组装和传输,是应用之间数据传输的载体。

用户接口层

用户接口层会完成 DO 和 DTO 的互转,完成微服务与前端应用数据交互及转换。Facade 服务会对多个 DO 对象进行组装,转换为 DTO 对象,向前端应用完成数据转换和传输。

视图对象 VO(View Object)

用于封装展示层指定页面或组件的数据。

DDD与微服务的关系

DDD 主要关注

从业务领域视角划分领域边界,构建通用语言进行高效沟通,通过业务抽象,建立领域模型,维持业务和代码的逻辑一致性。

微服务主要关注

运行时的进程间通信、容错和故障隔离,实现去中心化数据管理和去中心化服务治理,关注微服务的独立开发、测试、构建和部署。

DDD、中台和微服务的关系

中台是抽象出来的业务模型,微服务是业务模型的系统实现,DDD 作为方法论可以同时指导中台业务建模和微服务建设,三者相辅相成,完美结合。

img_04

仓储

为了解耦应用逻辑和基础资源,在基础层和上层应用逻辑之间会增加一层,这一层就是仓储层。一个聚合对应一个仓储,仓储实现聚合内数据的持久化。聚合内的应用逻辑通过接口来访问基础资源,仓储实现在基础层实现。这样应用逻辑和基础资源的实现逻辑是分离的。如果变更基础资源组件,只需要替换仓储实现就可以了,不会对应用逻辑产生太大的影响,这样就实现了应用逻辑与基础资源的解耦,也就实现了依赖倒置。

找不到聚合根时

关于聚合设计过程中的一些原则问题。大部分的业务场景我们都可以通过事件风暴,找到聚合根,建立聚合,划分限界上下文,建立领域模型。但也有部分场景,比如数据计算、统计以及批处理业务场景,所有的实体都是独立无关联的,找不到聚合根,也无法建立领域模型。但是它们之间的业务关系是非常紧密的,在业务上是高内聚的。我们也可以将这类场景作为一个聚合处理,除了不考虑聚合根的设计方法外,其它诸如 DDD 分层架构相关的设计方法都是可以采用的

领域服务的CRUD是不是都是操作聚合根或整个实体对象,比如我只想根据ID判断记录是否存在,或者返回个别字段,需要返回整个实体对象吗?

其实查询类业务可以不必经过聚合根和仓储。传统方法也可以了。 如果聚合数据比较多,会有延迟加载影响性能。 聚合根的主要目的是为了保证数据的一致性,这些场景一般在CU的场景。

DDD领域建模和传统开发模式的区别?

DDD

事件风暴--产品愿景--场景分析--领域建模--微服务拆分与设计。

DDD领域建模优先,领域建模的时基本不考虑数据模型和数据库实现。在 微服务具体落地的时候才考虑数据实体的设计。

传统

产品需求--需求分析--详细设计--ER模型(数据库模型)--UML设计(java类关系)

事务封装在哪一层?

按照业务逻辑来讲,封装到应用层或领域层都是可以的

领域层调用关系

img_02 img_03

参考地址:

https://www.processon.com/view/612351cfe0b34d5c8a3163d3?fromnew=1

About


Languages

Language:Java 100.0%