显示 / 隐藏 文章目录 ]

领域驱动设计

上次更新: 2023-12-26 10:58:22

参考文献

架构对比

从后端视角看,对比传统的三层架构,领域驱动

  • 充血复用了:领域对外的接口
  • 领域服务封装了:领域之间的联系

贫血模型->充血模型,降低了service层的负担,同时保证了业务迭代,entity的独立性

MVC

从数据库视角、分析实体出发,进行系统的构建

  • DAO 层
  • Service 层
  • Controller 层

后端 DDD

针对基础设施层,也可以考虑加入防腐层工厂

一个领域基本四层架构

  1. 用户层(user interface): 对标 controller,对外提供 web 服务、接口
  2. 应用层(application): 业务层,定义领域可以解决的问题 domainService
  3. 领域层(domain): 纯粹的描述业务实体
  4. 基础设施层(infra): 持久化层(Builder+repository)

同时,领域拆分带来如下两个致命问题

  • 领域化(微服务)后,数据库是分开的,导致实体调用链复杂,避免跨库查询
  • 避免分布式事务同步

前端 DDD

?? 这里存在一个问题,api 网关到底要不要挪到前端领域目录下来开发

  1. 用户层: web、mobile、mini-program …多端
  2. service: 暴露给 UI 组件的 domainSerivce,组织实体的状态流转
  3. entity: 实体、聚合实体、事件
  4. infra: api 请求、缓存、工具

工厂 Factory / Builder

  • 数据库表设计层面,如果要多对多的关系,只能很别扭的设计一个中间表。
  • 实体 A 适合 mongo文档形存储,实体 B 适合mysql关系型存储。对于 Builder 而言,直接在这个领域搓出来 A 和 BCRUD的实现方法

防腐层 Facade / Adaptor

也被成为适配器,隔离第三方/外部服务,例如场景,用户上传文件到阿里云、腾讯云

class UserUploadServiceFacade extends UserService {
  /**
   * 上传
   */
  async upload(oss) {
    if (oss === "ali") return "OK";
    if (oss === "tx") return "FAIL";
  }
}

领域模型

实体的贫血->充血是一个随着业务发展过程演变的结果,初期业务场景不明确,很难充血,瞎几把充血,纯属找难受。

是否需要引入聚合根(aggregate root)的概念,解决领域内实体独立、平铺关系带来的不方便,这个可能比较看心情

贫血模型(POJO)的问题

脱离了业务复杂度谈分层,好比抛开剂量谈毒性

领域对象里只有get/set方法,所有的业务逻辑都不包含在内,从而造成失忆症,从实体中,无法知道发生了那些业务,需要去 service层 里挨个梳理,一旦业务迭代、service 层直接开始变成屎山

充血模型 && 领域服务的关系

确保领域之间独立的,随着不断的充血,保证领域(实体)是独立发展的

假定两个 domain(实体):

  • 学生: 上课、做作业
  • 老师、全体起立、布置作业

假定老师调用全体起立,对应肯定要学生调用上课,这个就是 领域服务(domainService) 需要去处理的,他俩不能建立直接的联系。

  • 增加新的业务逻辑:在 domainService 增加新的方法。
  • 调整旧的业务逻辑:修改学生老师,内部具体的方法,domainService 完全不需要变化。

重拾纯粹的写作