DDD分层

简介: 为什么分层引用《领域驱动设计模式、原理与实践》为了避免将代码库变成大泥球(BBoM)并因此减弱领域模型的完整性且最终减弱可用性,系统架构要支持技术复杂性与领域复杂性的分离。引起技术实现发生变化的原因与引起领域逻辑发生变化的原因显然不同,这就导致基础设施和领域逻辑问题会以不同速率发生变化每一层都有各自的职责,显然这也是符合SRP的

为什么分层

引用《领域驱动设计模式、原理与实践》

为了避免将代码库变成大泥球(BBoM)并因此减弱领域模型的完整性且最终减弱可用性,系统架构要支持技术复杂性与领域复杂性的分离。引起技术实现发生变化的原因与引起领域逻辑发生变化的原因显然不同,这就导致基础设施和领域逻辑问题会以不同速率发生变化

每一层都有各自的职责,显然这也是符合SRP的

如何分层

DDD的标准形态

image.png

  1. User Interface是用户接口层,主要用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给Application层的接口
  2. Application层是应用层,负责多进程管理及调度、多线程管理及调度、多协程调度和维护业务实例的状态模型。当调度层收到用户接口层的请求后,委托Context层与本次业务相关的上下文进行处理
  3. Domain层是领域层,定义领域模型,不仅包括领域对象及其之间关系的建模,还包括对象的角色role的显式建模
  4. Infrastructure层是基础实施层,为其他层提供通用的技术能力:业务平台,编程框架,持久化机制,消息机制,第三方库的封装,通用算法,等等

根据DDD细化业务逻辑层

image.gifimage.png

模块

结合maven的module,项目中现在分了八个module

<modules>
    <module>generator-assist-dao</module> <!-- 生成的dao -->
    <module>generator-assist-client-api</module> <!-- swagger api yaml -->
    <module>assist-client-api</module> <!-- 生成的api -->
    <module>assist-controller</module> <!-- controller -->
    <module>assist-service</module> <!-- domain -->
    <module>assist-infrastructure</module> <!-- infrastructure -->
    <module>assist-common</module> <!-- 基础common -->
    <module>start</module> <!-- 启动入口及test -->
</modules>

start

入口模块

包结构:

  • start 只有一个启动类
  • test 单元测试

除了启动类,还有单元测试

generator-assist-dao

生成的dao类

包结构:

  • repository
  • model 与数据库对应的实体类
  • repository
  • mapper mybatis的mapper

现在实践落地时,这个模块是个空模块,why?

DDD中明确了repository概念,并属于domain层,但dao是对底层数据库的封装,具体实现类放在infrastructure层更合理

在COLA中,作者也是为了领域层的纯洁性,依赖反转了,repository接口定义在domain层,而实现在infra层

image.png

但在落地时,domain与infra出现了循环依赖,COLA把实现放在了app层,这样有些另类,所以暂时先把repository全部放在了service层

迷思:

1、基于mybatis的实现,mapper本身是接口,repository实现类放在domain层,不要接口,这样满足DDD分层规则,但离DIP差了一步

2、在《DDD之熵》中提过

DDD引入repository放在了领域层,一是对应聚合根的概念,二是抽象了数据库访问,,但DDD限界上下文可能不仅限于访问数据库,还可能访问同样属于外部设备的文件、网络与消息队列。为了隔离领域模型与外部设备,同样需要为它们定义抽象的出口端口,这些出口端口该放在哪里呢?如果依然放在领域层,就很难自圆其说。例如,出口端口EventPublisher支持将事件消息发布到消息队列,要将这样的接口放在领域层,就显得不伦不类了。倘若不放在位于内部核心的领域层,就只能放在领域层外部,这又违背了整洁架构思想

3、是不是有别的理论支撑解决问题2

generator-assist-client-api

为了生成api的swagger yaml文件

包结构:

  • swagger-spec
  • all swagger所有yaml文件的整合文件
  • apis swagger定义的api
  • models swagger定义的api中的model
  • swagger-templates 模板文件

assist-client-api

通过swagger生成的api接口与api中的model

包结构:

  • client
  • api swagger生成的api接口
  • model swagger生成的request,response对象

assist-controller

controller层,放置controller

包结构:

  • controller 所有的controller
  • xxljob xxljob补偿任务

按DDD分层规范,controller属于ui层,处理restful请求

  • 接受请求 —— 由spring提供能力
  • 请求格式校验及转换 —— 格式校验遵循java Validation规范
  • 权限校验 —— 由网关处理
  • 路由请求 —— 网关处理
  • 记录请求 —— 专门Accessfilter处理
  • 回复响应 —— 由spring提供能力

为什么还有一个xxljob包,从能力区分,xxljob放到infra层才对。这个原因类似generator-assist-dao模块,xxljob的handler需要调用application service,需要依赖service module

因此可以把xxljob作为远程请求的一个入口,与controller一样归在ui层

这儿引出一点思考,controller真的是ui层吗?能划分到别的层吗?

有几种设计思路

  1. ui层完全归属于大前端,不在后端,也就不在ddd中,后端都是从application service开始
  2. controller归于ui
  3. controller归于infra,controller毕竟是依赖具体底层框架能力的adapter

image.gifimage.png

controller是基于springboot的具体实现

从上面的分析,可以看出controller逻辑上是归到infra层,但物理上不能放到infra模块;也不能简单把controller看作MVC中的C,还有很多像xxljob样的入口

  1. 入口会有很多,如controller、xxljob,还有mq等等
  2. 还有进程内的,如event,应用层,基础设施层,领域层都有event,怎么区分event是个问题
  3. application serivce与domain service区分也常常给人带来烦恼

这儿是否可以借鉴《DDD之形》中的端口和适配器架构

image.png

把controller看作driving adapter,既然区分这么复杂,那可不可以简单点,加厚controller,整合入口与application service

简单点分成两部分:远程服务与本地服务

  • 远程服务:定义会跨进程服务,分为资源(Resource)服务、供应者(Provider)服务、控制器(Controller)服务与事件订阅者(Event Subscriber)服务
  • 本地服务:所有远程服务需要调用领域服务,必须经过本地服务才能调用;明确隔离外界与领域,防止领域模型外泄

assist-service

domain层,但现在还是三层结构的思路,什么类都有,app service,domain service,dto,event 甚至还有基础设施层类

包结构

  • BO
  • builder
  • common
  • component
  • convertor
  • domain
  • dto
  • event
  • interceptor
  • listener
  • model
  • repository
  • service
  • thrid
  • valid

assist-infrastructure

基础设施层

包结构

  • config 配置信息
  • adapter 外部调用封装
  • clients 外部调用实现
  • pl 服务接口的契约 published language
  • dp domain primitive 这是不是应该在domain层
  • common 公共类,(InvoiceType与InvoiceTypeEnum的问题)
  • event
  • publish 事件发布者,此包为空,直接依赖spring不需要自实现了
  • exception 异常类
  • gateway 网关,封装访问外部系统或资源行为的对象
  • api 外接接口
  • dto 外接接口dto
  • wechat 外部名称
  • local
  • pl
  • ports
  • clients 外部调用接口
  • repository
  • model
  • resources 资源
  • service 依赖外部的service
  • util 工具类

现在的包结构很丰富,最常见的包就是gateway,配合acl与外部交互

image.png

U表示上游(Upstream)的被依赖方,D表示下游(Downstream)的依赖方。防腐层(ACL)放在下游,将上游的消息转化为下游的领域模型

结合generator-assist-dao模块的问题,是否可以扩大ACL,而不仅限于gateway中,像资源库一样,不必完全遵循DDD只抽象repository,像访问第三方应用,缓存,消息都可以抽象出来,契合端口履行的职责一样


改造

<modules>
    <module>generator-assist-dao</module> <!-- 生成的dao -->
    <module>generator-assist-client-api</module> <!-- swagger api yaml -->
    <module>assist-client-api</module> <!-- 生成的api -->
    <module>assist-ohs</module> <!-- ohs -->
    <module>assist-service</module> <!-- domain -->
    <module>assist-acl</module> <!-- acl -->
    <module>start</module> <!-- 启动入口及test -->
</modules>

assist-controller

根据上面的分析,这一层可以更厚实些

改名为assist-ohs

OHS,open host service 开放主机服务,定义公开服务的协议,包括通信的方式、传递消息的格式(协议)

包结构

  • remote
  • controller
  • openapi
  • xxljob
  • subscribe
  • local
  • appservices
  • pl (plush language) request,response
  • convertor

assist-service

domain层

包结构

  • domain 领域对象
  • service 领域服务
  • factory 领域对象工厂
  • builder 领域对象构造器

assist-acl

扩大了基础设施层,隔离领域层与外部依赖,对所有外部环境一视同仁,无需针对资源库做特殊化处理,如此也可保证架构的简单性,repository、client、cahce...

领域层依赖port接口

包结构

  • config 配置信息
  • port 依赖外部接口
  • repository 数据库接口
  • client 第三方系统接口
  • publisher 消息接口
  • cache 缓存接口
  • adapter port的具体实现
  • repository
  • pl
  • client

总结

模块划分以及包结构还只是一家之言,一是有充足的理论体系支撑,不管按DDD标准,还是变形,更多地有理有据,与团队、也与自己达成一致;二是domain的抽象,一切都是为了领域模型的稳定性和扩展性,形只是表象

我们这个项目还是太注重了形,最重要的domain还是过弱


目录
相关文章
|
缓存 前端开发 中间件
DDD 领域驱动设计落地实践系列:工程结构分层设计
前面几篇文章中,笔者给大家阐述了 DDD 领域驱动设计的三大过程,重点围绕如何通过战略设计与战术设计进行 DDD 落地实践进行了详细的讨论,但是还没有涉及到工程层面的落地。实际上所有的这些架构理论到最后都是为了使得我们代码结构更加清晰,从而开发出 bug 少、扩展性强、逻辑清楚的应用。因此本文就是为了解决 DDD 领域驱动落地实践最后一公里问题,将我们分析出来的领域模型通过与工程结构的映射实现真正的落地。
DDD 领域驱动设计落地实践系列:工程结构分层设计
|
前端开发 测试技术 API
DDD领域驱动设计实战-分层架构及代码目录结构(上)
DDD领域驱动设计实战-分层架构及代码目录结构
1551 0
DDD领域驱动设计实战-分层架构及代码目录结构(上)
|
存储 自然语言处理 前端开发
领域驱动设计(DDD)-基础思想
一、序言     领域驱动设计是一种解决业务复杂性的设计思想,不是一种标准规则的解决方法。在领域驱动设计理念上,各路大侠的观点也是各有不同,能力有限、欢迎留言讨论。 二、领域驱动设计 DDD是什么 wiki释义:     领域驱动设计(英语:Domain-driven design,缩写 DDD)是一种通过将实现连接到持续进化的模型[1]来满足复杂
7571 0
|
存储 设计模式 缓存
DDD领域驱动设计实战-分层架构及代码目录结构(下)
DDD领域驱动设计实战-分层架构及代码目录结构
1729 0
DDD领域驱动设计实战-分层架构及代码目录结构(下)
|
前端开发 Java 数据库连接
领域驱动设计(DDD):分层架构
在应用系统开发中,采用严格的、单一的、真正的的分层架构是可以的,但实际上我们已经采用了多种架构模式设计系统。当多种不同范式的架构混合在一起,你会不会出现“指鹿为马”的现象呢?
领域驱动设计(DDD):分层架构
|
3月前
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
|
5月前
|
存储 消息中间件 JSON
|
6月前
|
缓存 Java 应用服务中间件
架构设计篇问题之通过DDD领域模型对服务进行拆分问题如何解决
架构设计篇问题之通过DDD领域模型对服务进行拆分问题如何解决
|
6月前
|
缓存 前端开发 安全
DDD中的分层架构
领域驱动设计(DDD)的分层架构演进为依赖倒置的四层模型,强调关注点分离。表现层(UI)展示信息并处理用户指令,应用程序层负责用例编排,与领域层交互但不含业务逻辑。领域层承载核心业务逻辑,包含领域模型和服务,确保业务正确性。基础设施层提供技术支撑,如数据库和缓存,服务于其他层。各层解耦,实现灵活的系统架构。
115 0
|
8月前
|
数据库
DDD架构浅谈
DDD架构浅谈
201 4