跳转到内容

分层与依赖倒置

用途:决定代码放哪一层、依赖方向怎么约束时查阅

DDD 的经典分层是四层,依赖只能指向领域层:

接口层(interfaces / adapter) 协议转换、参数绑定、结果装配
↓
应用层(application) 用例编排、事务边界、权限与幂等
↓
领域层(domain) 聚合、值对象、领域事件、领域服务、仓储接口
↑ 实现
基础设施层(infrastructure) ORM、SQL、消息、外部系统客户端、仓储实现

依赖倒置是关键:领域层定义仓储与外部能力的接口,基础设施层提供实现。于是“领域层不认识数据库”成为可验证的事实,而不是口号。

三层结构(UI/Service/DAO)只解决了代码归类,没有解决“业务规则与基础设施互相渗透”。当 Service 同时依赖 HTTP 客户端、SQL 映射与业务判断时,规则无法独立测试,换技术栈等于改业务。

四层 + 依赖倒置把“业务语义”变成最内层、最稳定的部分,外围都是可替换的适配。

domain/
order/ Order(聚合根), OrderLine, OrderStatus, OrderRepository(接口)
pricing/ PricePolicy, DiscountSpecification
application/
order/ PayOrderService, ShipOrderService, OrderEventPublisher(接口)
infrastructure/
persistence/ OrderRepositoryImpl, OrderPO, OrderAssembler
messaging/ RocketMQOrderEventPublisher
external/ RiskControlClientAdapter
interfaces/
web/ OrderController, 请求/响应模型
job/ mq/ 定时任务与消息入口,复用同一批应用服务

要点:入口不止 HTTP。同一用例必须能被定时任务、消息消费者复用,因此编排逻辑不能留在 Controller。

模型 用途 转换发生地
请求/响应模型 对外协议 接口层
领域模型 业务规则 领域层
持久化对象(PO) 表结构 基础设施层
投影/视图模型 查询展示 查询服务

转换次数多确实带来样板成本,但它换来的是“改表不动模型、改协议不动业务”。Assembler/映射器(MapStruct 类工具)负责这部分机械代码。

口头约定必然退化,需要机器检查:

  • 架构测试:断言 domain 包不依赖 infrastructure、不 import ORM/HTTP 包(ArchUnit、Dependency-Cruiser 类工具)。
  • 构建期校验:把架构测试接入 CI,违反即失败。
  • 模块边界:多上下文时用 Maven/Gradle 模块强制可见性,比包约定更硬。
  • 六边形/洋葱/整洁架构:同一思想的变体,差别在端口与适配器的显式程度(见分层架构演进相关词条)。
  • COLA:把四层与扩展点固化为骨架模板。
  • 资源库:依赖倒置的最常见落点。
误区 修正
领域层加 Spring/ORM 注解(@Table、@Entity 混用) 至少保证领域模型不携带表结构与 SQL 语义
四层变成“每层都写一遍” 简单模块可用简化结构,按子域价值分级投入
应用层直接操作 PO 通过仓储与聚合,PO 不出基础设施层
只分层不分上下文 层内仍是大泥球,跨模块随意调用
目录很深但依赖方向没约束 加架构测试,让违规在 CI 失败
  • Eric Evans《领域驱动设计》第 13 章(架构分层)
  • Vaughn Vernon《实现领域驱动设计》第 11 章
  • Robert C. Martin《Clean Architecture》(依赖规则章节)