欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Domain Driven Design项目架构

程序员文章站 2022-07-15 12:40:48
...

Domain Driven Design 目录结构层级

TreeView

├── Application.java
├── assembler                       DTO 组装器:将领域对象组装成 DTO
├── config                          配置文件目录
├── constant                        常量、枚举目录
├── domain                          领域层
│   ├── event                       领域事件:MQ event
│   ├── model                       领域对象
│   ├── repository                  领域仓库
│   ├── service                     业务领域服务
│   └── translator                  翻译器:将持久化层 Entity 或 DTO 翻译为领域对象。
├── infrastructure                  基础设施目录
│   ├── exception                   异常
│   ├── interceptor                 权限拦截
│   ├── repository                  领域层仓库具体实现
│   │   ├── dao                     mysql、oracle、mongodb
│   │   │   └── impl
│   │   ├── mock                    哑实现
│   │   │   └── impl
│   │   └── redis                   redis 缓存
│   │       └── impl
│   ├── runner                      程序启动初始化 runner
│   ├── task                        定时任务
│   ├── transport                   第三方服务交互
│   ├── util                        工具类
│   └── validation                  校验层
├── service                         应用服务层
└── ui                              对外开放的用户层,User Interface
    ├── controller                  控制器
    └── dto                         用于外部数据交互的 DTO
        ├── request
        └── response

框架说明

User Interface 用户层

用户层,对外以各种协议提供服务,包含:

DTO

包括 request 和 response 两部分,通过它定义入参和出参的契约,在 DTO 层可以使用基础设施层的 validation 组件完成入参格式校验

Controller

Controller 使用基础设施层公共组件完成通用的工作:

  • 通过注解 RequestMapping 添加 servlet 路由
  • 通过 interceptor 完成登录权限/角色校验
  • 简单的数据转换

Application Layer 应用层

Service

应用服务层,组合 domain 层的领域对象和基础设施层的公共组件,根据业务需要包装出多变的服务,以适应多变的业务服务需求。

应用服务层主要访问 domain 领域对象,完成服务逻辑的包装。

应用服务层也会访问基础设施层的公共组件,如 redis,完成领域消息的生产和获取等。

Assembler

组装器,负责将多个 domain 领域对象组装为需要的 DTO 对象,比如查询帖子列表,需要从Post(帖子)领域对象中获取帖子的详情,还需要从 User(用户)领域对象中获取用户的基本信息。

组装器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。

Domain Layer 领域层

业务领域层,是我们最应当关心的一层,也是最多变的一层,需要保证这一层是高内聚的。确保所有的业务逻辑都留在这一层,而不会遗漏到其他层。按照 DDD(domain driven design)理论,主要有如下概念构成:

Domain Entity

领域实体对象。有唯一标识(主键),可变的业务实体对象,有自己的生命周期。比如门店就是一个业务实体,它需要有一个唯一性业务标识表征(门店 ID),同时它的状态(开店/停业)和内容(名称/地址)可以不断发生变化。

Domain Value Object

领域值对象。可以没有唯一性业务标识,且一旦定义,就不可变的,它通常是短暂的。这和Java中的值对象(基本类型和String类型)类似。比如门店领域中,门店的位置信息可以理解为是一个值对象,不需要为门店的位置信息定义唯一标识,直接使用门店 ID就可以找到每个门店的位置信息。同时,它具有省/市/区详细地址几个属性,一旦任一个属性发生变化,则需要重建这个位置信息对象并赋值给门店实体的引用。

Domain Factory

领域对象工厂。用于复杂领域对象的创建/重建。重建是指通过 respostory 获取到对象后,重建或组装多个对象为领域对象。

Domain Service

领域服务。区别于应用服务,它属于业务领域层。

可以认为,如果某种方法无法归类给任何单一实体/值对象,则就为这些方法建立相应的领域服务即可。比如:门店收银服务,需要操作门店/会员两个实体。

传统意义上的 util static 方法中,只要涉及到业务逻辑的部分,都可以考虑归入domain service。

Domain Event

领域事件。领域中产生的一些消息事件,通过事件通知/订阅的方式异步操作,可以在性能和解耦层面得到好处。

Repository

领域仓库。我们将仓库的接口定义归类在 Domain 层,因为它和 Domain entity 联系紧密。User Interface 和基础实施的持久化层交互,都需要实现领域仓库接口对应的增删改查操作。

仓库的实际实现根据不同的存储介质而不同,可以是redis、mysql等。

鉴于存储介质可以同时存在有多套:redis、mysql,memory cache, 且各个存储介质的字段属性名不一致,因此需要使用translator来做翻译,将持久化层的对象翻译为统一的领域对象。

Translator

翻译器。将持久化层的对象翻译为统一的领域对象。

翻译器中不应当有业务逻辑在里面,主要负责格式转换、字段映射等职责。

Infrastructure Layer 基础设施层

基础设施层提供公共功能组件,供 controller、service、domain 层调用。

Repository impl

对 domain 层 repository 接口的实现,对应每种存储介质有其特定实现,如 mysql、oracle的 dao 等等。repository impl 会调用 mybatis、jooq、redis client完成实际的存储层操作。

Interceptor

权限校验器,判定客户端是否有访问该资源的权限。提供给User Interface层的Controller调用。

Exception

异常分类及定义,同时提供公共的异常处理逻辑,具体由ExceptionHandler实现。

Transport 第三方服务交互层

transport 完成和第三方服务的交互,可以有多种协议形式的实现,如http+json、二进制文件流,用于对第三方服务的请求和响应进行适配,提供一个防腐层的作用。

注意要点

各个 package 的详细解释参考上节框架说明,着重注意如下几点:

  • domain.repository 包里面只有仓库的接口定义,实际的实现交给了 infrastructure 中的 repository module,该做法被称作依赖倒置,好处在于确保 domain 层语义完整,同时对确保业务领域的一致性也有帮助,再者可以在 domain 实现内存形式的 repository 哑实现,从而让 domain 可以真正脱离掉 infrastructure

  • infrastructure.repository 作为仓库层,会将实体的增删改查操作委托给具体的存储层服务,如 mysql 对应的 dao 实现,还有 redis 的实现

  • 少写应用层 Service,多把业务逻辑封装到 Domain

  • Controller 要瘦,仅处理消息或数据转发,Model 仅处理领域逻辑。而 Service 通常负责领域间的交互及某些预处理(比如数据验证、权限验证等等)

Reference

相关标签: DDD 项目结构