DDD领域驱动设计实战-分层架构及代码目录结构

时间:2024-10-05 13:03:40

[](()1.1 分层架构的基本原则


每层只与位于其下方的层发生耦合。

[](()1.2 分层架构的分类


  • 严格分层架构(Strict Layers Architecture)

某层只能与其直接下层耦合,即我的奴隶的奴隶,不是我的奴隶。

  • 松散分层架构(Relaxed Layers Architecture)

允许任意上层与任意下层耦合。由于用户接口层和应用服务通常需要与基础设施打交道,许多系统都是该架构。

较低层有时也可与较高层耦合,但只限于采用观察者 (Observer)模式或者调停者(Mediator)模式场景。

较低层绝不能直接访问较高层。例如,在使用调停者模式时,较高层可能实现了较低 Java开源项目【/public/P7/Java/git】 层的接口,然后将实现对象作为参数传递到较低层。当较低层调用该实现时, 它并不知道实现出自何处。

[](()1.3 分层架构演进


[](()1.3.1 传统四层架构

将领域模型和业务逻辑分离出来,并减少对基础设施、用户界面甚至应用层逻辑的依赖,因为它们不属业务逻辑。将一个夏杂的系统分为不同的层,每层都应该具有良好的内聚性,并且只依赖于比其自身更低的层。

传统分层架构的基础设施层位于底层,持久化和消息机制便位于该层。

这里的消息包含

  • MQ消息

  • SMTP

  • 文本消息(SMS)

可将基础设施层中所有组件看作应用程序的低层服务,较高层与该层发生耦合以复用技术基础设施。即便如此,依然应避免核心的领域模型对象与基础设施层直接耦合

[](()1.3.2 改良版四层架构

[](()传统架构的缺陷

DDD初创开发团队发现,将基础设施层放在最底层存在缺点,比如此时领域层中的一些技术实现就很困难:

  • 违背分层架构的基本原则

  • 难以编写测试用例

何解?

使用依赖反转设计原则:低层服务(如基础设施层)应依赖高层组件(比如用户界面层、应用层和领域层)所提供的接口。

[](()应用依赖反转原则
  • 依赖反转原则后的分层方式:基础设施层在最上方,可实现所有其他层中定义的接口

依赖反转原则真的可以支持所有层吗? 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源

有人认为依赖反转原则中只存在两层:最上方和最下方,上层实现下层定义的抽象接口。因此上图的基础设施层将位于最上方,而用户接口层、应用层和领域层应作同层且都位于下方。对此大家可保留自己意见。

[](()2 各层职责

=====================================================================

[](()2.1 Interfaces(用户接口层)


一般包括用户接口、Web 服务等。

只处理用户显示和用户请求,不应包含领域或业务逻辑。

有人认为,既然用户接口需验证用户输入,就无可避免应该包含业务逻辑。事实上,用户接口所进行的验证和对领域模型的验证不同:对那些粗制滥造且只面向领域模型的验证行为,应该予以限制。

如果用户接口使用了领域模型中的对象,那么此时领域对象仅限于数据渲染展现。在采用这种方式时,可使用展现模型对用户接口与领域对象进行解耦。

由于用户可能是人,也可能是其他系统,有时用户接口层将采用开放主机服务的方式向外提供API。

用户接口层是应用层的直接用户。

用户接口层在于前后端调用的适配。若你的微服务要提供服务给很多外部应用,而对每个外部应用的入参出参都不同,你不可能开发一堆一对一的应用服务,这时Facade接口就起到了很好的作用,包括DO和DTO对象的组装和转换。

由于主要负责接入各种终端,所以该层有人也叫接入层。

实际开发中,我们都会感受到该层依附于应用层存在。随前后端分离,Restful API 流行,对简单的系统来说该层越来越弱化。对于有终端接入的系统来说,该层并不简单,需要处理各种协议适配:XMPP、websocket、MQTT 等。

复杂度不高时,我们往往把该层和应用层合并部署,主要凭开发经验和理解程度来决定。

存放用户接口层与前端交互、展现数据相关的代码。

前端应用通过这层接口,向应用服务获取展现所需的数据。

该层主要用来处理用户发送的Restful请求,解析用户输入的配置文件,并将数据传递给应用层。

数据的组装、数据传输格式以及Facade接口等代码都会放在这一层目录里。

封装应用服务和对外暴露接口。

[](()特点

  • 关心视图和对外的服务,Restful、页面渲染、websocket、XMPP 连接等

  • 如果没有多种用户端接入方式,可以和应用层合并

  • 对应到分布式系统中的网关、BFF、前台等概念

  • 只产生接入异常,例如数据校验,对应 HTTP 状态码 400、415 等

  • 一个应用可以有多个接入层

  • 接入层做和业务规则无关的 bean validation 验证

  • 准单体系统下,按照连接方式分包

该层指的是服务端用于适配端侧的部分,而非端侧本身。因为该层本就依赖应用层,无人使用接口在这里做依赖倒置,所有又被称作主动适配。

[](()1.1 细分结构


  • assembler、dto 和 façade

[](()facade

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

[](()dto

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

比如接收请求传入的数据CustomerDTO。

不同的对象在不同的层转换。用户接口层DTO和DO转换,应用层主要是DO,调外部微服务的服务的时候应用层有dto和do的转换。领域层与基础层之间,在基础层有DO和PO的转换。

在接口层定义DTO对象。数据可能来源于多个DO对象。

[](()assembler

实现DTO与DO间的相互转换和数据交换。

一般assembler与dto一同出现。比如创建用户时,将CustomerDTO转换为CustomerEntity。你可以在用户接口层创建DTO类和assembler类。在assembler类里完成映射。

[](()2.2 应用层


[](()特点

  • 关心处理完一个完整的业务

  • 该层只负责业务编排,对象转换,实际业务逻辑由领域层完成

  • 不关心【请求从何处来】,但是关心【谁来、做什么、有没有权限做】

即复制安全认证、权限校验

  • 集成不同的领域服务解决问题

应用层位于领域层之上,因为领域层包含多个聚合,所以它可协调多个聚合服务和领域对象完成服务编排和组合,协作完成业务。

  • 最终一致性(最终一致性对业务有侵入)事务放到这层

  • 对应到分布式系统中的中台等概念

  • 方法级别的功能权限控制放到这层

  • 只产应用异常,对应 HTTP 状态码 403、401

  • 准单体系统下,按照应用划分模块

主要包含应用服务,理论上不应有业务规则或逻辑,而主要是面向用例和流程相关的操作。

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

开发设计时,不要将本该放在领域层的业务逻辑放到应用层。庞大的应用层会使领域模型失焦,时间一长,微服务就会退化为MVC

应用服务是在应用层,负责

  • 服务的组合、编排、转发、转换和传递,处理业务用例的执行顺序以及结果的拼装,以粗粒度服务通过API网关发布到前端

  • 发送或订阅领域事件

[](()应用层代码目录结构

存放应用层服务组合和编排相关的代码。

应用服务向下基于

  • 微服务内的领域服务,或

  • 外部微服务的应用服务

完成服务的编排和组合

向上为用户接口层提供各种应用数据展现支持服务。

应用服务和事件等代码会放在这层目录。

[](()Event(事件)

主要存放事件相关代码。包括两子目录:

[](()publish

主要存放事件发布相关代码。比如发布用户创建事件给其它微服务。

[](()subscribe

主要存放事件订阅相关代码(事件处理相关的核心业务逻辑在领域层实现)。

虽然应用层和领域层都可进行事件的发布和处理,但为实现事件的统一管理,推荐将微服务内所有事件的发布和订阅处理都统一放到应用层,事件相关的核心业务逻辑实现放在领域层。通过应用层调用领域层服务,来实现完整的事件发布和订阅处理流程。

[](()Service(应用服务)

应用服务会对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。应用服务主要实现服务组合和编排,是一段独立的业务逻辑。

可以将所有应用服务放在一个应用服务类里,也可以把一个应用服务设计为一个应用服务类,以防应用服务类代码量过大。

比如内部服务->创建用户;外部服务->创建日志。

[](()2.3 领域层


主要包含聚合根、实体、值对象、领域服务等领域模型中的领域对象。

实现核心业务逻辑,通过各种校验保证业务正确性。领域层主要体现领域模型的业务能力,它用来表达业务概念、业务状态和业务规则。

领域模型的业务逻辑主要由实体和领域服务实现:

  • 实体采用充血模型 实现所有与之相关的业务功能。

实体和领域服务在实现业务逻辑上不是同级,当领域中的某些功能,单一实体或值对象无法实现,就会用到领域服务,它可组合聚合内的多个实体或值对象,实现复杂业务逻辑。

[](()3 Domain(领域层)

最后

每年转战互联网行业的人很多,说白了也是冲着高薪去的,不管你是即将步入这个行业还是想转行,学习是必不可少的。作为一个Java开发,学习成了日常生活的一部分,不学习你就会被这个行业淘汰,这也是这个行业残酷的现实。

如果你对Java感兴趣,想要转行改变自己,那就要趁着机遇行动起来。或许,这份限量版的Java零基础宝典能够对你有所帮助。

领域模型的业务逻辑主要由实体和领域服务实现:

  • 实体采用充血模型 实现所有与之相关的业务功能。

实体和领域服务在实现业务逻辑上不是同级,当领域中的某些功能,单一实体或值对象无法实现,就会用到领域服务,它可组合聚合内的多个实体或值对象,实现复杂业务逻辑。

[](()3 Domain(领域层)

最后

每年转战互联网行业的人很多,说白了也是冲着高薪去的,不管你是即将步入这个行业还是想转行,学习是必不可少的。作为一个Java开发,学习成了日常生活的一部分,不学习你就会被这个行业淘汰,这也是这个行业残酷的现实。

如果你对Java感兴趣,想要转行改变自己,那就要趁着机遇行动起来。或许,这份限量版的Java零基础宝典能够对你有所帮助。

[外链图片转存中…(img-r1FcPa9a-1650169995978)]