领域驱动设计CQRS落地

领域驱动设计落地

  • 写模型代码结构
  • interfaces 用户接口层
    • facade 接口
    • assembler 组装器
  • application 应用层
    • service 应用层服务
  • domain 领域层
    • Xxx 聚合
      • entity 领域对象
        • aggregate 聚合根
        • entity 实体
        • valobj 值对象
      • repository 仓储
        • po 持久化对象
        • mapper
        • service
        • facade 仓储接口
        • persistence 仓储实现
  • infrastructure 基础层
    • util 工具
    • constant 常量
  • 读模型代码结构
    • 读写模型主要差异
  • 问题汇总

写模型代码结构

com.myj.drug.xxx
├─Application.java
├─interfaces
|     ├─facade
|     |   └XxxController.java
|     ├─assembler
|     |     └PersonAssembler.java
├─infrastructure
|       ├─util
|       ├─constant
├─domain
|   ├─Xxx
|   |   ├─service
|   |   |    ├─XxxDomainService.java
|   |   |    └XxxFactory.java
|   |   ├─repository
|   |   |     ├─po
|   |   |     | └XxxPO.java
|   |   |     ├─persistence
|   |   |     |      └XxxRepositoryImpl.java
|   |   |     ├─mapper
|   |   |     |   └XxxMapper.java
|   |   |     ├─service
|   |   |     |   └XxxServiceImpl.java
|   |   |     ├─facade
|   |   |     |   └XxxRepositoryInterface.java
|   |   ├─event
|   |   |   ├─XxxEvent.java
|   |   |   └XxxEventType.java
|   |   ├─entity
|   |   |   ├─aggregate
|   |   |   ├─entity
|   |   |   ├─valobj
├─application
|      ├─service
|      |    └XxxApplicationService.java

interfaces 用户接口层

用户接口层是前端应用与微服务应用层的桥梁,通过 Facade 接口封装应用服务,适配前端并提供灵活的服务,完成 DO 和
DTO(Query/Command) 相互转换

当应用服务接收到前端请求数据时,组装器会将 DTO(Query/Command) 转换为 DO。当应用服务向前端返回数据时,组装器会将
DO 转换为 DTO。

facade 接口

实际上对应的就是三层架构的Controller层

assembler 组装器

将 DTO(Query/Command) 转换为 DO 或者 DO 转换为 DTO(Query/Command) 的工具类

application 应用层

应用层位于领域层之上,主要负责组合和编排领域层服务

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

service 应用层服务

调用领域层服务或者调用其他微服务的服务

domain 领域层

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

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

领域模型的业务逻辑主要是由实体和领域服务来实现的,其中实体会采用充血模型来实现所有与之相关的业务功能。

实体主要实现单一业务逻辑

领域服务主要实现多实体之间的复杂业务逻辑

Xxx 聚合

某个高度业务内聚性的聚合,里面包含聚合根、实体、值对象、领域服务等领域模型中的领域对象

entity 领域对象

主要包含聚合根、实体、值对象

aggregate 聚合根

主要包含聚合根实体,负责管理聚合内所有实体的生命周期,例如初始化,持久化等等

entity 实体

主要包含实体,依附于聚合根实体的生命周期,但是有唯一标识,一般需要落库

valobj 值对象

主要包含值对象,没有唯一标识,一般跟着聚合根落库,是聚合根对应表的部分字段,没有实际的对应表结构

repository 仓储

一个聚合一个仓储,实现聚合数据的持久化。领域服务通过仓储接口来访问基础资源,由仓储实现完成数据持久化和初始化。仓储一般包含:仓储接口和仓储实现

po 持久化对象

与表一一对应的java对象

mapper

使用mybatis-plus框架,每个PO对象都用对应的Mapper对象

service

使用mybatis-plus框架,每个PO对象都用对应的Service对象,其实单用mapper也可以,但是生成这个对象可以使用很多已经实现的数据持久化功能,可以提高开发效率

facade 仓储接口

为了解耦业务逻辑和基础资源,在基础层和领域层之间增加一层仓储服务,实现依赖倒置。通过这一层可以实现业务逻辑和基础层资源的依赖分离。在变更基础层数据库的时候,只要替换仓储实现就可以了,上层核心业务逻辑不会受基础资源变更的影响,从而实现依赖倒置。

但是用于为了方便领域内的不同聚合之前拆分,把仓储实现也放到了领域层里面

主要是实现数据持久化的统一接口,一般只包含save,findById,remove 3个方法,对应的是聚合根的持久化,从内存恢复聚合根,或者删除等等

由于业务常常包含更新操作,如果每次都通过save进行更新操作,会产生不必要的字段更新,或者导致数据不一致,所以特地再加上update方法,用于聚合根的更新持久化

除了上述的4个方法,可能还会有其他的查询方法,而这个查询方法不是面向接口调用,而是其他领域服务需要用的查询方法

persistence 仓储实现

对仓储接口的实现,主要基于上面所说的 mybatis-plus 的 mapper,service 去实现

infrastructure 基础层

基础层是贯穿所有层的,它的作用就是为其它各层提供通用的技术和基础服务,包括第三方工具、驱动、消息中间件、网关、文件、缓存以及数据库等。比较常见的功能还是提供数据库持久化。但是由于上面说的,为了方便领域模型的聚合拆分,也可以把仓储实现放到领域层

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

util 工具

代码中需要用到的工具类

constant 常量

代码中需要用到的常量

读模型代码结构

├─DrugSystemQueryApplication.java
├─interfaces
|     ├─facade
|     |   └UserController.java
├─infrastructure
|       ├─utils
|       |   └DefaultUtil.java
|       ├─enums
|       |   └Default.java
├─application
|      ├─service
|      |    └UserQueryApplicationServiceImpl.java
|      ├─repository
|      |     ├─service
|      |     |    └UserServiceImpl.java
|      |     ├─po
|      |     | └UserPO.java
|      |     ├─persistence
|      |     |      └UserRepositoryImpl.java
|      |     ├─mapper
|      |     |   └UserMapper.java
|      |     ├─facade
|      |     |   └UserRepository.java

读写模型主要差异

  1. 读模型代码结构删掉了领域层
  2. 仓储统一放到应用层
  3. 其余于写模型基本一直

问题汇总

  1. 在DDD的原则里,repository操作的都是聚合根,repository的作用就是把内存中的聚合根持久化,或者把持久化的数据还原为内存中的聚合根。repository中一般也只有getById,save,remove几个方法。例如取消订单的场景,我其实只需要更新order的状态等少数几个字段,但是如果调用repository的save方法,就会把订单其他字段以及订单明细数据都更新一次,这样就会造成性能影响,以及数据冲突的问题。

    在repository增加只更新部分字段的方法,就是上文我们说的update方法,虽然这样会对repository有一定的污染,但是实现比较简单

  2. 消费mq的逻辑应该属于那一层?

    消息订阅方一般在应用层监听和接受事件数据

  3. 订单父单和子单设计成一个聚合好,还是2个聚合好?

    一般来说订单和订单明细是在一个聚合里面的

  4. 聚合根与领域服务在职责上有些重叠了,在实现的时候如何选择?

    理论上,聚合根方法和领域服务都可以组合多个实体对象完成复杂的领域逻辑。但为了避免聚合根的业务逻辑过于复杂,避免聚合根类代码量过于庞大,建议聚合根除了承担它的聚合管理职能外,只作为实体实现与聚合根自身行为相关的业务逻辑。而将跨多个实体的复杂领域逻辑放在领域服务中实现。简单聚合的跨实体领域逻辑,可以考虑在聚合根方法中实现

  5. 仓储为什么不是放在基础层?

    理论上仓储都应该放到基础层的。将它们放到领域层的聚合目录下主要是基于以下考虑,一个聚合会有一个仓储,以后微服务的演进基本上会以聚合为单位进行重组或者拆分,所以在代码拆分的时候以聚合目录进行整体迁移就可以了,这样在代码上不会花太多的时间

  6. 复杂的查询怎么办?

    复杂的查询不建议放在领域层去解决。可以采用CQRS,也就是常说的读写分离的模式,或者直接在应用层完成复杂查询。本次我们实现的就是读写分离模式。

  7. 领域事件怎么实现?

    同一个项目的微服务内不同聚合之间采用事件总线方式(SpringEvent),不同项目的微服务之间采用消息队列模式

  8. 事务控制放在哪里合适?

    聚合内可以在领域服务采用事务机制,聚合之间或微服务之间可以在应用服务采用事务机制

  9. 是不是每个微服务都要有自己单独的数据库?

    没有特殊考虑的话,建议一个微服务一个数据库,但是由于目前我们不动数据库,所以还是共用一个数据库,但是代码层面要把他当成不同数据库来对待,不同微服务的数据访问,一定要通过不同微服务所暴露的服务来获取,不可以直接操作其他微服务的数据表

  10. 使用Dubbo应该将哪一层的服务进行封装api

    个人认为应该将应用层服务进行api封装,不同微服务需要互相访问的时候,导入相应的api包即可,里面包含了req和resp等入参和出参的对象

  11. 想问下在工厂里可以使用MapStruct来简化PO和Entity的数据初始化和持久化的转换吗

    这个工具应该可以用来做DO和PO的转换,不过本次我们没用使用

  12. 复杂查询放在哪里?

    由于查询一般没有太多的业务逻辑和规则控制,所以一般复杂的查询都会放在应用层

  13. 层间调用关系

    依赖原则是外层依赖内层。例如不能从领域层去调应用层的服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/25597.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

暑假刷题第23天--8/6

3748. 递增子串 - AcWing题库 #include<iostream> #include<string> const int N200005; int a[N]; using namespace std; int main(){int t;cin>>t;for(int q1;q<t;q){int n;cin>>n;string s;cin>>s;int cnt1;a[1]1;for(int i2;i<n;i){i…

Java spring boot 全解Camunda 7,从 0 到 1 构建工作流平台——第二节:Spring boot 简单集成

目录 1. 成果展示2. 环境准备3. 项目构建3.1 项目结构3.2 引入Camunda 依赖3.3 启动spring boot 程序3.4 启动 web app 程序 引言&#xff1a;当今技术发展迅猛&#xff0c;企业对于业务流程的高效管理和自动化需求也日益增长。在这个背景下&#xff0c;Spring Boot和Camunda7成…

4 三组例子,用OpenCV玩转图像-AI-python

读取&#xff0c;缩放&#xff0c;旋转&#xff0c;写入图像 首先导入包&#xff0c;为了显示导入matplotlib/为了在matplotlib显示 导入CV2/查看版本 导入图片/查看图片类型 图片数组 数组大小 对于opencv通道顺序蓝色B、绿色G、红色R matplotlib通道顺序为 红色R、绿色G、蓝…

【JavaEE】Spring Boot - 项目的创建和使用

【JavaEE】Spring Boot 开发要点总结&#xff08;1&#xff09; 文章目录 【JavaEE】Spring Boot 开发要点总结&#xff08;1&#xff09;1. Spring Boot 的优点2. Spring Boot 项目创建2.1 下载安装插件2.2 创建项目过程2.3 加载项目2.4 启动项目2.5 删除一些没用的文件 3. Sp…

解决Element Plus中Select在El Dialog里层级过低的问题(修改select选项框样式)

Element Plus是Vue.js的一套基于Element UI的组件库&#xff0c;提供了丰富的组件用于构建现代化的Web应用程序。其中&#xff0c;<el-select>是一个常用的下拉选择器组件&#xff0c;但在某些情况下&#xff0c;当<el-select>组件嵌套在<el-dialog>&#xf…

macbook怎么卸载软件?2023年最新全新解析macbook电脑怎样删除软件

macbook怎么卸载软件&#xff1f;2023年最新全新解析macbook电脑怎样删除软件。关于Mac笔记本如何卸载软件_Mac笔记本卸载软件的四种方法的知识大家了解吗&#xff1f;以下就是小编整理的关于Mac笔记本如何卸载软件_Mac笔记本卸载软件的四种方法的介绍&#xff0c;希望可以给到…

HCIP IPV6

一、IPV6升级特点联系和区别 IPV4--->IPV6 1、全球单播地址 ---- IPV4地址下的公有地址 V6下没nat 2、可聚合性&#xff08;IANA组织对全球的地址进行合理分配&#xff09; 3、多宿主---一个物理接口可以同时拥有多个不同网段的IPV6地址&#xff1b;但不同接口不能在…

Linux进程信号

全文目录 概念什么是Linux信号&#xff1f;信号行为&#xff08;core dump&#xff09;如何理解信号被进程保存&#xff1a;信号发送的本质&#xff1a; 产生信号1. 终端按键&#xff08;组合键&#xff09;变成信号&#xff1a;2. 通过系统调用接口向进程发送信号3. 软件条件产…

【笔记】湖仓一体架构演进与发展

https://www.bilibili.com/video/BV1oF411F7rQ/?spm_id_from333.788.recommend_more_video.0&vd_sourcefa36a95b3c3fa4f32dd400f8cabddeaf

Camunda 7.x 系列【2】开源工作流引擎框架

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址&#xff1a;https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 前言2. 开源工作流引擎框架2.1 jBPM2.2 Activ…

setmap使用

目录 set使用 set的模板参数 构造函数 成员函数 insert iterator ​编辑 find count pair pair 的模板参数 make_pair multiset使用 multiset 的模板参数 set 与 multiset 的区别 count map使用 map 的模板参数 构造函数 insert iterator find ​编辑 cou…

【SpringCloud】深入探究Eureka:构建微服务架构中的高效服务发现系统

&#x1f468;‍&#x1f4bb;博主主页&#xff1a;小尘要自信 在现代的软件开发中&#xff0c;微服务架构已经成为了一个热门的话题。微服务架构的一个关键组成部分就是服务发现。而在服务发现领域&#xff0c;Eureka无疑是一个备受推崇的解决方案。本篇博客将为您介绍什么是E…

git面试题

文章目录 git经常用哪些指令git出现代码冲突怎么解决你们团队是怎么管理git分支的如何实现Git的免密操作 git经常用哪些指令 产生代码库 新建一个git代码库 git init下载远程项目和它的整个代码历史 git clone 远程仓库地址配置 显示配置 git config --list [--global]编辑配置…

Python IDE

Python IDE 本文为大家推荐几款款不错的 Python IDE&#xff08;集成开发环境&#xff09;&#xff0c;比较推荐 PyCharm&#xff0c;当然你可以根据自己的喜好来选择适合自己的 Python IDE。 PyCharm PyCharm 是由 JetBrains 打造的一款 Python IDE。 PyCharm 具备一般 Pyt…

机器学习——SMO算法推导与实践

一、 硬间隔-SMO算法推导 明天再说&#xff0c;啊。。。。感觉天空明朗了很多&#xff0c;即使现在已经很晚了 还是要打开柯南&#xff0c;看看电视&#xff0c;等待天气预报所说的台风天吧&#xff01; 一时之间&#xff0c;忽然失去了用markdown语法写下推导过程的勇气。。。…

ip网络广播系统网络音频解码终端公共广播SV-7101

SV-7101V网络音频终端产品简介 网络广播终端SV-7101V&#xff0c;接收网络音频流&#xff0c;实时解码播放。本设备只有网络广播功能&#xff0c;是一款简单的网络广播终端。提供一路线路输出接功放或有源音箱。 产品特点 ■ 提供固件网络远程升级■ 标准RJ45网络接口&…

推荐几款主流的Css Reset

CSS Reset CSS Reset&#xff08;CSS重置&#xff09;是一种技术&#xff0c;用于消除不同浏览器之间默认样式的差异&#xff0c;以确保网页在各个浏览器中的显示一致性。由于不同浏览器对元素的默认样式有所不同&#xff0c;使用CSS Reset可以将这些默认样式归零或统一&#x…

基于粒子群改进BP神经网络的血压评估系统,血压预警系统,pso-bp神经网络

目录 摘要 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数, BP神经网络的传递函数 粒子群算法的原理及步骤 基于粒子群算法改进优化BP神经网络的血压评估系统 matlab代码 代写下载链接:https://download.csdn.net/downlo…

echarts 柱状图 实例

实例效果&#xff1a; 代码&#xff1a; draw(data1, data2,data3) {var option {// backgroundColor: rgb(10,36,68),tooltip: {trigger: axis,axisPointer: {type: shadow,},formatter: function (params: any, ticket: any, callback: any) {const item params[0];var str…

VB+SQL采购管理系统设计与实现

摘 要 本系统是基于为轴承企业采购部门开发的系统。课题主要采用自上而下的结构化程序设计方法与面向对象方法相结合的方法,致力于达到标准的现代化物流管理要求。帮助轴承企业采购部门全面实现电子化、自动化、标准化的现代化先进管理模式。 该系统使用Visualbasic.net编程…