DDD开发模式说明

1. 文档目标

本文档基于项目脚手架说明整理,目的是统一团队对 DDD(领域驱动设计)开发模式的理解,并指导在当前工程中的实际落地方式,包括:

  • 工程分层职责
  • 目录结构设计
  • 命名规范
  • 接口规范
  • 服务调用链路
  • 微服务间调用方式
  • 公共组件依赖原则

2. 为什么要采用DDD

传统三层架构通常按 Controller -> Service -> DAO 组织代码,虽然上手快,但随着业务复杂度上升,容易出现以下问题:

  • 业务规则集中堆积在 Service 层
  • 数据模型成为系统核心,业务语义表达不足
  • 各层耦合较强,模型或规则变化时影响范围大
  • 代码偏向"表结构驱动",不利于复杂业务演进

DDD 的核心目标,是让"领域模型"而不是"数据库结构"成为软件设计中心,把复杂业务规则沉淀到领域层,提升系统的可维护性、可扩展性和表达能力。


3. DDD四层架构

当前项目采用的是经典 DDD 四层架构,并结合适配器/防腐层思想进行工程化落地。

3.1 用户接口层(User Interface / Adapter / API)

职责:

  • 对外暴露接口
  • 接收请求、返回响应
  • 完成参数转换
  • 作为消息、事件等入口

特点:

  • 不承载核心业务规则
  • 只负责协议适配和调用应用层
  • 是外部系统访问当前服务的入口

3.2 应用层(Application)

职责:

  • 定义系统要完成的业务用例
  • 编排领域对象完成业务流程
  • 协调领域层与外部接口之间的调用关系
  • 处理事务边界、输入输出 DTO、远程依赖抽象

特点:

  • 应用层要"薄"
  • 不应沉淀复杂业务规则
  • 更关注"做什么",而不是"业务规则本身是什么"

3.3 领域层(Domain)

职责:

  • 承载核心业务概念、业务规则和状态
  • 表达领域模型
  • 定义实体、值对象、领域服务、仓储接口、领域事件

特点:

  • 是业务软件的核心
  • 领域层不依赖其他业务层
  • 业务变化优先体现在领域层

3.4 基础设施层(Infrastructure / Infra)

职责:

  • 提供技术实现能力
  • 实现仓储、远程调用、消息发送、配置、持久化、过滤器、异常等
  • 实现领域层定义的接口

特点:

  • 面向技术细节
  • 通过依赖倒置为上层提供能力
  • 隔离框架、中间件、数据库、远程服务等具体实现

4. 数据模型驱动与领域模型驱动对比

4.1 数据模型驱动

典型特点:

  • 接口定义和实现集中在 API 层
  • 业务逻辑集中在 Service 层
  • DAO 和数据库模型成为核心依赖
  • 表结构变化容易传导到服务层甚至接口层

问题:

  • 业务语义弱
  • 规则分散
  • 演进成本高

4.2 领域模型驱动

典型特点:

  • 接口能力仍由 API 层提供
  • 业务逻辑拆分为"应用层编排 + 领域层规则"
  • 领域层是业务核心
  • 基础设施层通过依赖倒置实现技术隔离

优势:

  • 业务规则归位
  • 分层清晰
  • 复杂业务更容易扩展和维护

5. 项目中的DDD工程结构

项目脚手架的核心结构可以理解为两个部分:

  • sdk:对外暴露接口契约
  • biz:服务内部业务实现

5.1 SDK层

用于给其他服务依赖,主要包含北向接口定义:

  • adapter/api:对外 API 定义
  • adapter/request:请求模型
  • adapter/response:响应模型

作用:

  • 统一服务间接口契约
  • 便于微服务之间通过 jar 方式复用接口定义

5.2 Biz层

承载真正的业务实现,主要分为以下几层:

adapter

职责:

  • API 实现
  • 参数转换
  • 消息/事件入口

常见子目录:

  • impl
  • convertor
  • listener
application

职责:

  • 定义应用服务
  • 编排领域服务
  • 定义输入输出 DTO
  • 定义对外部服务依赖的抽象接口

常见子目录:

  • dto/input
  • dto/output
  • service
  • service/impl
  • remote
domain

职责:

  • 承载业务核心模型
  • 按领域划分子域
  • 定义实体、领域服务、仓储接口、事件接口

常见子目录:

  • 领域/entity
  • 领域/service
  • 领域/service/impl
  • 领域/repository
  • 领域/event
infra

职责:

  • 实现技术能力与外部依赖
  • 实现仓储、远程服务、事件发送等接口
  • 提供异常、过滤器等基础能力

常见子目录:

  • adapter/api.impl
  • adapter/event
  • adapter/repository
  • adapter/repository/convertor
  • adapter/repository/mapper
  • adapter/repository/po
  • adapter/repository/impl
  • exception
  • filter
common

职责:

  • 放置本服务公共能力
  • 包括常量、枚举、工具类

常见子目录:

  • constants
  • enums
  • utils

6. 分层职责边界

为了避免职责混乱,开发时应遵守以下边界。

6.1 Adapter层应该做什么

可以做:

  • 接收 HTTP / MQ / 事件请求
  • 调用 Convertor 做对象转换
  • 调用 Application Service
  • 返回统一响应

不要做:

  • 编写复杂业务规则
  • 直接操作数据库
  • 绕过应用层直接操作领域对象

6.2 Application层应该做什么

可以做:

  • 编排业务流程
  • 调用多个领域服务
  • 控制事务边界
  • 调用 remote 接口抽象
  • 输出 DTO

不要做:

  • 沉淀复杂领域规则
  • 直接依赖数据库 Mapper
  • 编写面向技术实现的细节代码

6.3 Domain层应该做什么

可以做:

  • 表达实体状态和行为
  • 编写领域规则
  • 定义仓储接口
  • 定义领域事件
  • 实现领域服务

不要做:

  • 依赖 Controller、Mapper、Feign、数据库表结构等技术细节
  • 出现明显的接口协议对象,如 Request/Response/PO

6.4 Infra层应该做什么

可以做:

  • 实现 Repository
  • 实现 RemoteService
  • 实现 EventPublisher
  • 落地持久化、远程调用、消息发送

不要做:

  • 反向主导业务规则
  • 直接把 PO 当成领域实体使用

7. 推荐调用链路

7.1 Command操作

写操作推荐链路:

adapter -> application -> domain -> infra

说明:

  • 接口层接收请求
  • 应用层编排业务
  • 领域层处理核心规则
  • 基础设施层落地持久化或外部交互

7.2 Query操作

查询操作可采用以下方式:

  1. adapter -> application -> domain -> infra
  2. adapter -> application -> infra
  3. adapter -> infra

其中:

  • 普通查询建议优先使用第 2 种,降低复杂度
  • 报表类、纯展示类查询可以使用第 3 种
  • 如果查询包含明确业务规则,仍建议经过 domain

原则:

  • 读操作可以适度简化
  • 写操作尽量保持完整 DDD 链路

8. 微服务之间的调用方式

项目中推荐使用 OpenFeign 进行微服务调用。

8.1 服务提供方

  • interface-api 模块打成独立 jar
  • 对外暴露 API、Request、Response 等契约模型

8.2 服务消费方

  • 引入服务提供方的接口 jar
  • infrastructure 层实现远程调用
  • 通过 application.remote 中定义的抽象进行依赖倒置

原则:

  • 应用层依赖抽象
  • 基础设施层依赖具体远程实现
  • 避免应用层直接依赖 Feign 细节

9. 命名规范

9.1 类命名规范

  • interface-api***Api***Request***Response
  • interface/adapter***Controller***Convertor
  • application***AppService***AppServiceImpl***InputDto***OutputDto***RemoteService***AppConvertor
  • domain***DomainService***DomainServiceImpl***Entity***Repository
  • infrastructure***RepositoryImpl***RemoteServiceImpl***Mapper***Po***InfraConvertor

9.2 包命名规范

统一格式:

com.gientech.ri.***.模块名

说明:

  • *** 为工程名简写
  • 启动模块可不追加模块名

10. REST接口设计规范

10.1 URL规范

统一格式:

/{系统名(可选)}/{应用名}/api/{业务模块(可选)}/{资源}(s)/{id}/{动作(可选)}?{param}={xxx}

10.2 HTTP动词规范

  • GET:查询
  • POST:新增、复杂动作、批量处理
  • PUT:更新
  • DELETE:删除

10.3 非CRUD动作规范

对于批量操作、复杂业务动作,不使用模糊参数,而是在 URL 中显式表达动作,例如:

  • POST /arch-govern/api/govern/tasks/batch-update
  • POST /arch-govern/api/govern/task/010/execute

这样做的好处:

  • 语义清晰
  • 单一职责明确
  • 接口边界稳定

11. 公共组件依赖原则

项目中的公共组件遵循以下原则:

  • basedirectory 属于底层公共组件,不依赖其他三个公共组件
  • basedirectory 之间可以相互依赖
  • 计划项目需求 等公共组件,除依赖基础组件和目录组件外,最好不要相互依赖
  • 如果必须依赖其他服务,优先通过其 application 层提供的服务进行调用

核心思想:

  • 控制依赖方向
  • 避免横向耦合
  • 防止公共组件网状依赖

12. 开发落地建议

在当前脚手架下,推荐按以下顺序开发功能:

  1. 明确业务领域和子域边界
  2. domain 中定义实体、仓储接口、领域服务
  3. application 中定义用例、DTO 和编排逻辑
  4. adapter 中提供接口实现与对象转换
  5. infra 中补齐仓储实现、远程调用、消息实现
  6. 按命名规范和分层规范补充测试代码

13. 总结

本项目的 DDD 模式,本质上是在经典四层架构基础上,引入:

  • 领域核心建模
  • 应用层编排
  • 基础设施隔离
  • 适配器/防腐层思想

它强调的不是"目录拆分",而是"职责拆分":

  • API层负责接入
  • Application层负责编排
  • Domain层负责业务规则
  • Infra层负责技术实现

对于复杂业务系统,这种模式比传统三层架构更适合持续演进,也更有利于团队协作和代码治理。

相关推荐
喵喵蒻葉睦2 小时前
力扣 hot100 和为K的子数组 哈希&前缀和
java·数据结构·算法·leetcode·前缀和·哈希算法
Amour恋空2 小时前
SpringBoot使用SpringAi完成简单智能助手
java·spring boot·后端
TDengine (老段)2 小时前
TDengine IDMP 组态面板 —— 图元
大数据·数据库·人工智能·物联网·时序数据库·tdengine
wuyikeer2 小时前
SQL2000在win10上安装的方法
运维·服务器
马里马里奥-2 小时前
文献阅读:LinkAlign:面向真实世界大规模多数据库文本转SQL任务的可扩展模式链接方法
数据库·sql
FITA阿泽要努力2 小时前
《通过实战SQL学会CTE、CURRENT_DATE、CURDATE()、DATE_SUB与缩进&注释的问题》
数据库
爬山算法2 小时前
MongoDB(42)如何使用$project阶段?
数据库·mongodb
lang201509282 小时前
18 Byte Buddy 进阶:揭秘方法委托中的“歧义解析”机制
java·byte buddy
倔强的石头1062 小时前
数据库迁移 TCO 全景账本:MySQL 替代中的隐性成本与工程化工具链实测
数据库·mysql·kingbase