Sam's Notes | Sam Blog

梦想还是要有的,万一实现了呢

0%

DDD

主要内容

  • DDD基础理论
  • 代码结构

DDD基础理论

领域驱动设计

参考书籍:

领域驱动设计精粹

领域驱动设计与模式实战

设计原则

  • SRP(Single responsibility principle):单一职责原则,一个module只有一个原因修改
  • OCP(Open/closed principle):开放-关闭原则,开放扩展,关闭修改
  • LSP(Liskov substitution principle):里氏替换原则,子类型必须能够替换它们的基类型
  • ISP(Interface segregation principle):接口隔离原则,你所依赖的必须是真正使用到的
  • DIP(Dependency inversion principle):依赖导致原则,依赖接口而不是实现(高层不需要知道底层的实现)

模型演化

  • 分层架构
    系统按不同职责组织成有序层次,由于这种划分往往比较容易界定,也算是最常见和最受欢迎的一种架构,有一个说法是:“如果你不知道要用什么架构,那就用它。

MVC

缺点:

+ 底层是基础设施层, 领域层依赖于基础设施层
  • 依赖倒置

依赖倒置的原则(DIP)由Robert C. Martin :

高层模块不应该依赖于底层模块,两者都应该依赖于抽象
抽象不应该依赖于实现细节,实现细节应该依赖于接口

事实上已经没有分层概念了。无论高层还是底层,实际只依赖于抽象,整个分层好像被推平了。

  • 六边形架构/整洁架构

六边形架构/整洁架构

也是一种分层架构,只不过不是上下或左右,而是变成了内部和外部。
从外环到内环,软件的层级逐渐升高。
外环(low level)依赖内环(high level)。

代码结构

代码

代码实例

1
2
3
4
5
6
7
8
9
10
11
12
DDD
├── adapter
│ └── 输出适配器
├── api
│ └── 输入适配器
├── application
│ └── 应用层
├── domain 领域层
│ ├── model
│ │ └── 领域模型层
│ └── service
│ └── 领域服务层

adapter 输出适配器

系统内部,具体技术实现;
仓库,文档,缓存,消息机制,领域事件发布/监听;

一般如下类型:

  • persist
  • message
  • eventListener

api 输入适配器

  • 对外接口,传统 controller API 层, 程序入口,客户端调用;

一般如下类型:

  • controller

application 应用层

应用服务,传统 service 层, 一般跟场景(用例)有关。

一般如下类型:

  • handler/service
  • command 场景 POJO

一个场景(用例),对应一个command,对应一个handler

domain 领域层

一般如下类型:

  • model 领域模型
  • service 领域服务
  • factory/builder 工厂
    复杂对象构建, 比如 主键ID使用snow或美团leaf

model 领域模型层

一般如下类型:

  • entity
  • repository
  • event
  • 值对象

service 领域服务层

  • 协调多个聚合,并且是领域逻辑,不放在应用服务层, 放在领域服务层
  • 算法,策略,保持实体和值对象的单一原则,可以提炼出来变成领域服务
  • 访问数据库等外部资源

方寸之间

领域服务

  • 当领域遜辑放某-一个聚合里不合适,需要协调多个聚合,但由于是领域逻辑,放在应用服务里不合适的时候,可以放到领域服务里;

  • 需要访问数据库等外部资源的业务逻辑,不建议聚合里,可以放到领域服务里

  • 有些算法、策略代码,为了保持实体和值对象的职责单- - ,可以提炼出来变成领域服务( 领域服务类的命名不-定都要以Service结尾)

  • 不涉及事务处理

富领域模型双刃剑

  • 好处

分开对待本质复杂度和偶然复杂度,核心业务逻辑被封装在领域对象里,内聚,容易保持一致性, 且容易维护和扩展。

此外,容易测试,且代码和测试都可以作为文档。

  • 坏处

对象引用多, 内存占用大, 影响吞吐量

通过聚合在贫富间取得平衡

聚合是一组相关领域模型的集合,是用来封装业务的不变性。同时强迫大家尽可能的简化领域模型之间的关联关系。在贫富之间寻找平衡。

聚合的主要原则包括:

  • 聚合是-致性边界,聚合根负责执行业务规则,改变边界内的任一对象的状态都不能违反整个聚合的所有业务规则;

  • 聚合根有全局标识,聚合边界内的其他实体只有局部标识,聚合边界外的对象,只能持有聚合根的标识不能引用聚合根对象,也不能持有聚合内部对象或标识

  • 聚合具有整体的生命周期,删除聚合根,聚合内的所有对象都需要删除

  • 只有聚合根能从持久化系统内查询得到,边界内的对象只能从聚合根导航访问

聚合根和数据一致性

应用服务作为事务一致性边界,一个事务里不能涉及到两个聚合的修改,跨聚合的数据应该使用最终一致。

但最终一致性成本很高。

实例代码中,基于内存实现同步的领域事件发布和订阅。这样,实际上两个聚合根的更改基于同一个本地数据库事务。

但由于使用了事件驱动,在代码层面,两个聚合根的更新是解耦的,在需要最终一致性的时候容易重构。