【实践篇】DDD脚手架及编码规范 | 京东云技术团队

news2024/9/28 1:17:34

一、背景介绍

我们团队一直在持续推进业务系统的体系化治理工作,在这个过程中我们沉淀了自己的DDD脚手架项目。脚手架项目是体系化治理过程中比较重要的一环,它的作用有两点:

(1)可以对新建的项目进行统一的规范;

(2)对于指导老项目进行DDD的改造提供指导。

本文主要是梳理和总结了DDD脚手架使用中的编码规范以及遇到的问题。

二、脚手架的理论基础

DDD相关的应用架构有很多种,比如四层架构,洋葱架构,六边形架构,整洁架构等。这些应用架构都有各自的特点和不同。但是他们的总体思想都是相似的,主要是通过分层来实现功能和关注点的隔离。达到的目标是领域层不依赖任何其他外部实现,这样就能保证核心业务逻辑的干净和稳定。

左图是整洁架构的示意图,左图为分层,右图表示各个分层的变化频率和抽象层级。整洁架构主要分为4层:

(1)Frameworks&Drivers层:这一层表示系统依赖的外部系统,比如数据库、缓存、前端页面等。这一层是变化频率最高的,也是需要和我们的核心业务逻辑做隔离的。

(2)Interface Adapters层:这一层是一个适配层,主要负责外部系统和内部业务系统的适配,这一层的主要作用就是外部系统和内部系统的适配和协议转换。

(3)Application Business Rules: 应用业务规则层,可以理解为用例层,这一层表示整个应用可以提供哪些用例级别的功能和服务。这一层也是对第4层中的核心业务规则的编排层。

(4)Enterprise Business Rules: 这一层就是最为核心的业务逻辑层,这一层不包含任何和技术相关的内容,只包含业务逻辑。

三、脚手架介绍及使用

使用命令如下:

mvn archetype:generate     
-DarchetypeGroupId=com.jd.jr.cf     
-DarchetypeArtifactId=ddd-archetype     
-DarchetypeCatalog=local     
-DarchetypeVersion=0.0.1-SNAPSHOT     
-DinteractiveMode=false     
-DgroupId=com.jd.demo.test        //从这一行开始需要根据项目名称修改 
-DartifactId=demo-test     
-Dversion=1.0.0     
-Dpackage=com.jd.demo.test     
-DappName=demo-test  -s D:/git/settings.xml      // 本地 git配置文件


生成完的项目结构如下:

|--- adapter                     -- 适配器层 应用与外部应用交互适配
|      |--- controller           -- 控制器层,API中的接口的实现
|      |       |--- assembler    -- 装配器,DTO和领域模型的转换
|      |       |--- impl         -- 协议层中接口的实现
|      |--- repository           -- 仓储层
|      |       |--- assembler    -- 装配器,PO和领域模型的转换
|      |       |--- impl         -- 领域层中仓储接口的实现
|      |--- rpc                  -- RPC层,Domain层中port中依赖的外部的接口实现,调用远程RPC接口
|      |--- task                 -- 任务,主要是调度任务的适配器
|--- api                         -- 应用协议层 应用对外暴露的api接口
|--- boot                        -- 启动层 应用框架、驱动等
|      |--- aop                  -- 切面
|      |--- config               -- 配置
|      |--- Application          -- 启动类
|--- app                         -- 应用层
|      |--- cases                -- 应用服务
|--- domain                      -- 领域层
|      |--- model                -- 领域对象
|      |       |--- aggregate    -- 聚合
|      |       |--- entities     -- 实休
|      |       |--- vo           -- 值对象
|      |--- service              -- 域服务
|      |--- factory              -- 工厂,针对一些复杂的Object可以通过工厂来构建
|      |--- port                 -- 端口,即接口
|      |--- event                -- 领域事件
|      |--- exception            -- 异常封装
|      |--- ability              -- 领域能力
|      |--- extension            -- 扩展点
|      |       |--- impl        -- 扩展点实现
|--- query                       -- 查询层,封装读服务
|      |--- model                -- 查询模型
|      |--- service              -- 查询服务


整体的分层架构图如下:

四、脚手架编码规范

1、Api模块编码规范:

  • Api模块是专门用于定义对外接口的模块,所以这个模块中只包含接口定义,出入参定义,尽量不依赖其他包。
  • Api中的接口定义类以xxxxResource(或者xxxxService)结尾。这条规范完全是为了和老的应用保持一致。
  • Api接口的入参尽量不要使用Java中的原子类型(Primitive Type), 需要将入参定义为单独的类。 最好是继承现有的BaseRequest类。
  • Api接口的出参统一使用泛型类对真实的返回类型进行包装。
  • 出入参类都以DTO结尾。
  • 出入参中尽量不适用枚举值类型的成员变量。

2、Adapter/Controller模块编码规范:

  • 这一层中需要将出入参的DTO和业务层的VO/DO对象进行转换。
  • 这一层不要包含任何的业务逻辑,只包含参数转换和业务无关的校验逻辑。
  • 接口返回值缓存类的逻辑,可以放在这个模块中实现,因为这个动作不包含业务逻辑。

3、App模块编码规范:

  • 这个模块中的类统一以Case结尾。
  • 这一层主要是对底层业务逻辑进行编排。可以直接调用Domain层的port定义。跨域的服务调用也可以放在这个模块中。
  • 这一层可以直接调用Domain模块中定义的Repository服务。
  • 事务处理:如果是跨多个聚合的业务逻辑需要放在一个事务中,需要在这一层开启和提交事务。

4、Domain层编码规范:

  • DomainService命名统一以Service为后缀。
  • Entity实体类的命名不用后缀。 值对象类的定义统一以VO结尾。
  • DomainService逻辑中可以调用Repository和Port中定义的接口。
  • DomainService可以操作多个聚合,实体和值对象。
  • Entity实体类可以有构造函数,builder,getters。 不要直接放开所有属性的setters,防止业务代码随意修改实体的属性。
  • 编写业务逻辑需要遵守原则:优先将业务逻辑放在Entity和VO中,然后才是放在聚合中,最后才放在DomainService中。
  • 依赖反转原则:Domain层依赖的外部接口都要定义在Domain模块的port包中。Domain层只面向接口编程,不依赖接口实现类。

5、Adapter/Repository和Rpc模块编码规范:

  • Repository实现类中需要将接口入参中的DO对象转换为PO对象后再调用数据库存储。
  • Repository和聚合的关系是一对一的关系。一个Repository有唯一的对应的聚合。
  • 如果Repository中需要开始事务可以在Repository实现类中开启事务。
  • Rpc层最好是对外部接口的出参和入参定义一个防腐层对象,命名统一以DTO结尾。

五、常见问题及解决办法

Q1、Api模块对外提供的jar包中是否要引用其他应用的jar包?

A1: 有一些场景,A应用的Api接口的入参需要引用其他应用的包中的类。比如A应用发出了一个事件,B应用提供了一个接口来处理这个事件,那B应用是否要引用A应用的包中的事件定义类呢? 理想情况,最好是B应用定义一个自己的类,这样B应用就不会依赖A应用的包。

Q2、Api包中是否能包含枚举类的定义?

A2:最好不要在Api包中对外暴露内部的枚举值定义。因为枚举值是需要在Domain模块中定义和使用的,不适合通过jar包的形式暴露给外部。 如果确实有需求要暴露给外部应用(比如为了让接口调用方方便的知道入参中的值有哪些),可以将枚举类的定义放在同一的common包中。这样Domain模块和对外提供的jar包都可以引用common包。

Q3、数据存储是否要使用统一版本号?

A3: 对于新应用,最好是使用统一的版本号,这样在更新数据库的时候就可以统一使用版本号当做乐观锁。但是对于遗留系统而言,启用版本号的成本比较高,因为需要梳理所有对实体进行变更的点,要求所有的点都统一使用版本号。所以要根据情况来确定是否使用。

Q4、对于一些偏流程性的业务,频繁的调用外部rpc接口。如果每个rpc接口都添加一个防腐层对象的话,会降低开发效率。是否可以不定义防腐层对象?

A4:最好是定义防腐层对象,短期可能降低一些开发效率,但是从长期和代码标准话的角度看,还是值得的。

作者:京东科技 史纪军

来源:京东云开发者社区 转载请注明来源

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

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

相关文章

【校招VIP】java语言考点之类的加载过程

考点介绍: 类的加载过程在校招面试中是个高频考点。类只有在要运行的时候才会被加载进JVM,即编译后只有需要到这个类的时候才会把他加载进JVM运行,这种动态加载是依靠反射来实现的,一般来说一个class只会被加载一次...... 『java语言考点之…

oracle 启停操作

1. 监听端口启停 # 根据实际情况 切换至oracle用户 su - oracle# 状态查看 lsnrctl stat# 启动1521端口监听 lsnrctl start# 关闭1521监听 lsnrctl stop 2. 数据库服务启停 # 立即关闭服务 shutdown immediate# 启动服务 startup

前端需要理解的 JavaScript 知识

1 关于JavaScript JavaScript(JS)是单线程的、基于原型的、弱类型的、动态类型的、轻量的、支持面向对象/命令式/声明式编程的、头等函数的、多范式的、解释性(直译式或即时编译)的、也可在非浏览器环境下使用的动态脚本语言。Ja…

AI创作助手:介绍 TensorFlow 的基本概念和使用场景

目录 背景 环境测试 入门示例 背景 TensorFlow 是一个强大的开源框架,用于实现深度学习和人工智能模型。它最初由 Google 开发,现在已经成为广泛使用的机器学习框架之一。 TensorFlow 简单来说就是一个用于创建和运行机器学习模型的库。它的核心概念…

安装ssl证书有什么意义?

相信很多网站用户对ssl证书都有一定的了解,它就像是身份证,同时也能保障网站的安全,尤其是一些交易平台,不泄露客户的隐私。为了保证在线客户的隐私信息的安全,中小型企业一定要明白安装ssl证书的重要性。下面JoySSL的…

Nginx实践之使用MaxMind的GeoIP2实现处理不同国家的访问

场景: 因防火墙到期,原网站接入的需要回到公网的解析,经过分析,站点只需要国内访问。于是想到了用地址过滤,目前比较火的是nginx的allow和deny,另外一种是geoip,geoip2 区别: GeoIP用于通过IP获…

ssm班主任助理系统源码和论文

ssm班主任助理系统源码和论文054 开发工具:idea 数据库mysql5.7 数据库链接工具:navcat,小海豚等 技术:ssm 1、课题背景 由于当今社会科学技术不断发展,科学技术不断发展,21世纪的今天是一个讲究质量和效率的时代&#xff0…

8、Vue 核心技术与实战 智慧商城项目 DAY8~10

1、 项目演示 2、 项目收获 3、 创建项目 4、调整初始化目录 1、删掉(1)assets中的文件(2)components中的文件(3)views中的文件2、(1)修改路由配置,默认的路由是个空数组即可,把路由…

医疗中心疫苗保存,隐藏这招你不知道的技法!

由于疫苗对温度敏感,如果没有在规定温湿度下储存,疫苗的药性很可能发生改变,从疫苗制造的部门到疫苗使用的现场之间的每一个环节,都可能因温度过高而失效。 为了保证从生产、贮存、运输、分发到使用的整个过程中,疫苗始…

销售客户拜访时要获取客户的哪些信息?

俗话说“马无夜草不肥”,信息是销售过程中的关键,谁拿下了信息战谁就占据了市场的制高点。 只有深入了解客户各方面信息,才能有针对性地提供解决方案并达成销售目标。根据我们团队的多年经验,我总结出了两个需要重点考虑的方面&a…

OLED透明屏费用分析报告:尺寸和分辨率对价格的影响

OLED透明屏作为一项创新的显示技术,其透明度和高清晰度的特点使其在各个领域得到了广泛应用。 然而,对于许多人来说,了解OLED透明屏的费用以及其性价比是购买和应用该技术时的重要考虑因素。 本文将通过相关数据和报告,详细介绍…

什么是自助服务知识库?

自助服务知识库是一个自助式客户服务库,其中包含有关产品、服务或主题的内容。通常,它是公司自助服务门户或网站的一部分。 客户每次需要查找产品相关问题的答案、产品使用/故障排除的分步指导或了解有关公司产品的更多信息时,都可以访问知识…

PHP实践:获取网络上图片的长宽以及图片类型

🏆作者简介,黑夜开发者,全栈领域新星创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责…

高温老化实验箱选什么软件好

为什么要使用老化监控软件采集数据,老化房老化室软件的十大优点如下: 1. 自动化控制:老化室软件可以实现对老化房的自动化控制,无需人工干预,提高了老化过程的准确性和稳定性。 2. 精确的参数设置:软件可以…

长胜证券:ETF规模持续增长!增量资金加速入市,正“借道埋伏”A股

据了解,ETF是助力普惠金融、服务居民财富办理的重要产品。ETF产品参加门槛较低,且费率较低,便于投资者买卖。ETF产品不断创新,不断满意投资者日益增加的财富办理需求,已成为一般投资者共享我国资本商场开展红利的重要方…

RTSP流媒体服务器EasyNVR安防视频云平台视频流分享到iOS设备上但画面出现拉伸的问题解决方案

EasyNVR是基于RTSP/Onvif协议的视频接入、处理及分发的安防视频云平台,可提供丰富且灵活的视频能力,包括:设备接入、实时视频直播、录像、云存储、录像回放与检索、告警、级联等。平台部署轻快、功能强大,在安防视频监控领域有着广…

【LeetCode75】第三十五题 统计二叉树中好节点的数目

目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 给我们一棵二叉树,让我们统计这棵二叉树中好节点的数目。 那么什么是好节点,题目中给出定义,从根节点…

如何提取视频的音频到手机?这个音频提取方法很简单

提取视频中的音频可以帮助您获得视频的声音部分,而无需观看整个视频。这对于那些只想听视频的声音或想将视频的声音与其他音频内容混合使用的人来说非常方便。此外,提取音频也可以为需要创建音频剪辑或混音的音频制作者提供帮助。那么怎么提取呢&#xf…

创建git项目并提交

1.创建仓库 2.点击创建 3复制gitee码云的HttpS连接 4 提交上传 打开项目并点击菜单栏上的【CVS】–》【Import into version control】–》【Create Git Repository】创建本地仓库 在打开的【Create Git Repository】对话框内选择本地仓库的位置,这里我选择…

如何在Pandas中根据条件替换列中的值

错误语法:data["当日平均温"].loc[data["当日平均温"] > ub] ub 正确语法:df.loc[ df["column_name"] "some_value", "column_name" ] "value" dataframe.loc[]函数可以用一个条件…