DDD领域驱动设计实战-分层架构及代码⽬录结构,2021年腾讯Java⾼级⾯试题
及答案
该层指的是服务端⽤于适配端侧的部分,⽽⾮端侧本⾝。因为该层本就依赖应⽤层,⽆⼈使⽤接⼝在这⾥做依赖倒置,所有⼜被称作主动适配。
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(领域层)
============================================================================
存放领域层核⼼业务逻辑相关的代码。
可包含多个聚合代码包,共同实现领域模型的核⼼业务逻辑。聚合以聚合内的实体、⽅法、领域服务和事件等代码会放在该层⽬录。
领域层包括⼀个或多个聚合的实体类、事件实体类、领域服务以及⼯⼚、仓储相关代码。⼀个聚合对应⼀个聚合代码⽬录,聚合之间在代码上完全隔离,聚合之间通过应⽤层协调。
Domain 由⼀或多个聚合包构成,共同实现领域模型的核⼼业务逻辑。
聚合内的代码模型是标准和统⼀的,包括:entity、event、repository、service ⼦⽬录
Aggregate(聚合)
聚合软件包的根⽬录,可根据实际项⽬的聚合名称命名,⽐如权限聚合。在聚合内定义聚合根、实体和值对象以及领域服务之间的关系和边界。聚合内实现⾼内聚的业务逻辑,它的代码可以独⽴拆分为微服务。
以聚合为单位的代码放在⼀个包⾥的主要是为业务内聚,更是为以后微服务之间聚合的重组。聚合之间清晰的代码边界,可让你轻松地实现以聚合为单位的微服务重组。
实例
⽐如进⼊⽤户聚合⽬录下(如CustomerAggregate)。
假设这样⼀个场景,主播账户作为⼀个聚合,优惠券模块作为⼀个聚合。那主播选券的命令属于主播账户聚合。然后主播账户⾥的优惠券就是这个聚合⾥的值对象。
如果有多个聚合, ⽐如聚合根A和聚合根B, 从业务的⾓度讲,可以接受AB间数据的最终⼀致性,但从数据展⽰的⾓度考虑, A和B是有强关联性的,也就是说在页⾯上,他们总是⼀起在页⾯的某部分出现, 那可以分别调两个聚合的领域服务,然后将两个聚合根的DO对象转换为⼀个DTO,就可以给前端提供包含两个聚合数据的数据服务了。
细分结构
Entity(实体)
存放聚合根、实体、值对象以及⼯⼚模式(Factory,⼯⼚模式主要是实现复杂聚合的实体的数据初始化。如果实体太多,聚合根处理起来会很复杂,通过⼯⼚⼀次初始化)相关代码。实体类采⽤充⾎模型,同⼀实体相关的业务逻辑都在实体类代码中实现。跨实体的业务逻辑代码在领域服务中实现。⽐如⽤户聚合根。
Event(事件)
存放事件实体以及与事件活动相关的业务逻辑代码。⽐如创建⽤户的事件。
Service(领域服务)
存放领域服务代码。⼀个领域服务是多个实体组合出来的⼀段业务逻辑。你可以将聚合内所有领域服务都放在⼀个领域服务类中,你也可以把每⼀个领域服务设计为⼀个类。如果领域服务内的业务逻辑相对复杂,我建议你将⼀个领域服务设计为⼀个领域服务类,避免由于所有领域服务代码都放在⼀个领域服务类中,⽽出现代码臃肿的问题。领域服务封装多个实体或⽅法后向上层提供应⽤服务调⽤。⽐如具体的创建⽤户逻辑,⽐如⽤户是否重复校验,分配初始密码等。
Repository(仓储)
存放所在聚合的查询或持久化领域对象的代码,通常包括仓储接⼝和仓储实现⽅法。为了⽅便聚合的拆分和组合,设定原则:⼀个聚合对应⼀个仓储。⽐如将⽤户信息保存到数据库。
按DDD分层架构,仓储实现本应属基础层代码,但为在微服务架构演进时,保证代码拆分和重组的便利性,把聚合仓储实现的代码放到聚合包内。这样,如果需求或设计发⽣变化导致聚合需要拆分或重组,就可将包括核⼼业务逻辑和仓储代码的聚合包整体迁移,轻松实现微服务架构演进。
2.4 基础层
为其它各层提供通⽤技术基础服务:
三⽅⼯具
驱动
MQ
API⽹关
⽂件
缓存
DB
最常⽤的
基础层包含基础服务,它采⽤依赖反转,封装基础资源服务,实现应⽤层、领域层与基础层解耦。
MVC架构由于上层应⽤对DB强耦合,很多公司在架构演进最怕换DB,⼀旦更换,可能需重写⼀堆代码。
但采⽤依赖反转,应⽤层即可通过解耦保持独⽴核⼼业务逻辑。当DB变更,只需更换DB基础服务。
4 Infrastructure(基础层)
====================================================================================
主要存放基础资源服务相关的代码,为其它各层提供的通⽤技术能⼒、三⽅软件包、数据库服务、配置和基础资源服务的代码都会放在这⼀层⽬录⾥。
Infrastructure 的代码⽬录结构有:config 和 util 两个⼦⽬录。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。