-
可以对新建的项目进行统一的规范 -
对于指导老项目进行DDD的改造提供指导
-
Frameworks&Drivers层:这一层表示系统依赖的外部系统,比如数据库、缓存、前端页面等。这一层是变化频率最高的,也是需要和我们的核心业务逻辑做隔离的。 -
Interface Adapters层:这一层是一个适配层,主要负责外部系统和内部业务系统的适配,这一层的主要作用就是外部系统和内部系统的适配和协议转换。 -
Application Business Rules: 应用业务规则层,可以理解为用例层,这一层表示整个应用可以提供哪些用例级别的功能和服务。这一层也是对第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 -- 查询服务
整体的分层架构图如下:
-
Api中的接口定义类以xxxxResource(或者xxxxService)结尾。这条规范完全是为了和老的应用保持一致。 -
Api接口的入参尽量不要使用Java中的原子类型(Primitive Type), 需要将入参定义为单独的类。最好是继承现有的BaseRequest类。 -
Api接口的出参统一使用泛型类对真实的返回类型进行包装。 -
出入参类都以DTO结尾。 -
出入参中尽量不适用枚举值类型的成员变量。
-
这一层中需要将出入参的DTO和业务层的VO/DO对象进行转换。 -
这一层不要包含任何的业务逻辑,只包含参数转换和业务无关的校验逻辑。 -
接口返回值缓存类的逻辑,可以放在这个模块中实现,因为这个动作不包含业务逻辑。
-
这个模块中的类统一以Case结尾。 -
这一层主要是对底层业务逻辑进行编排。可以直接调用Domain层的port定义。跨域的服务调用也可以放在这个模块中。 -
这一层可以直接调用Domain模块中定义的Repository服务。 -
事务处理:如果是跨多个聚合的业务逻辑需要放在一个事务中,需要在这一层开启和提交事务。
-
DomainService命名统一以Service为后缀。 -
Entity实体类的命名不用后缀。值对象类的定义统一以VO结尾。 -
DomainService逻辑中可以调用Repository和Port中定义的接口。 -
DomainService可以操作多个聚合,实体和值对象。 -
Entity实体类可以有构造函数,builder,getters。不要直接放开所有属性的setters,防止业务代码随意修改实体的属性。 -
编写业务逻辑需要遵守原则:优先将业务逻辑放在Entity和VO中,然后才是放在聚合中,最后才放在DomainService中。 -
依赖反转原则:Domain层依赖的外部接口都要定义在Domain模块的port包中。Domain层只面向接口编程,不依赖接口实现类。
-
Repository实现类中需要将接口入参中的DO对象转换为PO对象后再调用数据库存储。 -
Repository和聚合的关系是一对一的关系。一个Repository有唯一的对应的聚合。 -
如果Repository中需要开始事务可以在Repository实现类中开启事务。 -
Rpc层最好是对外部接口的出参和入参定义一个防腐层对象,命名统一以DTO结尾。