Domain Driven Design项目架构

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值