DDD项目结构(领域驱动设计)
DDD项目结构(领域驱动设计)
DDD项目结构(领域驱动设计)这种工程结构模型我接触了一年多,虽然每个人都有不同的理解,不过相比于MVC的工程结构,对于代码结构的清晰度,还有业务逻辑的分层方面,DDD都是优于MVC的。
尽管每个人对DDD模型的理解不同,但大家的思路总归是一致的(api、app、domain、infra),争议之处在于一些包放置的层级以及代码分层。
因此这里记录了对DDD工程结构的理解,以后我会不定期更新。
互相分享,互相学习。
总体概览
先晒出工程目录:
├─ api
│ ├─ controller
│ ├─ dto
├─ app
│ ├─ service
│ │ └─ impl
├─ domain
│ ├─ entity
│ ├─ service
│ │ └─ impl
│ ├─ repository
│ │ └─ impl
│ ├─ vo
├─ infra
│ ├─ config
│ ├─ constant
│ ├─ util
│ ├─ mappper
│ ├─ feign我会从下到上依次进行说明。
infra(基础设施层)
infra主要负责基础资源的定义。
基础资源常见类型为以下几种:
- config
- constant
- utils
- mapper
- feign
这里涉及到一个问题,什么是基础资源?
基础资源指的是工程用到的工具类、配置类、常量类(枚举类)、获取数据的接口(包括从数据库中获取数据的mapper层和从外部接口获取数据的feign层)。
基础资源构成了工程的骨架和常用工具,一个工程只有骨架(config,mapper,feign)齐全,工具(constant, utils)完备,才能正常进行开发。
domain(领域层)
领域层是整个DDD模型的核心,是上层(app层)和下层(infra层)的桥梁
领域层主要分为三个包
- entity
- repository
- service
- vo
entity对应的是实体层,也是业务对象的核心,数据库中表映射的对象就是实体类。
entity的基础意义是作为数据库表映射的实体对象。允许存在对实体类属性操作的封装方法。
比如说,现在有需求对这个实体进行一个取消操作。取消操作需要设置cancelFlag=0,并且设置取消日期和操作人。这时就可以对这个取消操作封装成一个方法。
repository层对应的是资源库层,代表对资源的操作。在DDD模型中,mapper层仅能代表和数据库的交互操作,而DDD中资源这个概念并不仅仅指的是数据库的数据,还包括网络资源(http)和文件资源等。
并且业务逻辑中,最常见的是头行结构的业务数据,可在repository层进行头行操作的业务逻辑封装。
service层这里指的是领域service层(domain),并非是应用service层(app)。
这里的service层是对业务操作进行一个粗粒度的封装,但不是完全封装。完全封装是应用service层要做的事。
举个例子:有一个订单系统,有自动审批配置,如果自动审批开启,则要在订单提交后进行自动审批。
领域service层要做的就是封装两个方法,提交和审批方法,而判断是否进行自动审批,则应该交给应用service层去做
VO只负责数据流转,没有处理逻辑,负责对网络资源,数据库资源进行接收
app(事务层)
app层处理实际需求,进行事务控制,流程调度。
只有一层
- service
app层需要对domain层,infra层的接口进行调用,并且要求实现对业务逻辑的完全封装(例如在domain层举的订单系统审批例子)。
另外,是否需要事务控制则需要根据需求进行具体分析
api(接口层)
api层做接口。负责与外界的数据交互。
api层主要分为两个包
- controller
- dto
controller层是整个系统和外部交互的端口,因此权限控制,资源访问方法控制(GET、POST、PUT、DELETE)、数据校验控制,数据防篡改控制,都要在这一层做
dto层则是负责接收外部系统传递的数据。
对于权限控制和数据控制方面,可在infra层定义相关的拦截器进行处理,controller层仅需要增加校验注解。这一层要尽可能的轻。
结语
在这种工程结构下,DTO的作用实际上是被严重弱化了,仅仅起到了一个接收数据的作用。
还有事务方面,repository和domain是一定要加事务控制的,app则要根据具体业务逻辑来做。
之前接过一个需求,要求订单生成完毕后传入另一个系统,无论另一个系统返回什么都要生成订单,此时app层就需要做独立事务处理,因此是否要求事务控制之还是要看实际应用场景是什么。
另外还有数据库事务隔离级别的问题,比如mysql的默认级别是可重复读(使用mvcc的方式控制版本),tidb的默认级别是快照隔离,也因此被坑过。不过这个不在本文讨论范围之内,只是要强调事务控制要看具体场景,脱离实际应用都是空谈。