引言

如何应对复杂软件开发?以unix为例

  1. 封装与抽象
  2. 分层与模块化
  3. 基于接口通信
  4. 为扩展而设计

linux运作漫画图)理论太干,辅以图片说明,同时让听众思考buffer

DDD介绍

  • 历史
  • 简介
    • 一种思维模式
    • 本质是方法论
    • by experience

由数据模型驱动设计 引入

一门语言的基本语法和编程技巧、一个ORM框架的使用方法及基本的sql编写能力——就这三板斧,足以!

这种设计方式的弊端:mvc模式

  • 缺乏边界
  • 贫血模型,transactional scripts
  • 由贫血症导致的失忆症(即业务代码意图不明
  • 跟数据库表定义强耦合
  • 缺乏对领域模型的思考和建模

正式引入DDD

怎么玩?

  1. 团队+领域专家讨论,统一语言
  2. 战略设计:划分界限上下文
  3. 战术设计:界限上下文落地
  4. 根据实际情况迭代、重构界限上下文,往复循环

对比数据模型驱动模式

虚拟钱包案例

mvc模型

  • 贫血模型,不够面向对象
  • 业务类命名太泛,缺乏边界,职责模糊,后续很容易被腐化
  • 数据库表模型散落到业务层,灵活性差

ddd建模

  • 充血模型,更加面向对象
  • 使用门面模式,屏蔽数据库操作细节
  • service层变逻辑变简单,领域逻辑交给领域层做,service层只负责编排

使用原则

  • ddd非银弹,不应滥用
  • 设计系统时,多考虑墨菲定律
  • 划分系统时,多考虑康威定律

总结

  • 数据驱动设计有诸多弊端
  • DDD天生为控制大型软件的复杂度而生

领域建模流程

  1. 事件风暴。一起头脑风暴,完善用例图
  2. 抽象。提炼出系统的主要行为
  3. 划分界限上下文。主要区分好核心域、支撑域、公共域
  4. 界限上下文映射。主要区分好各个上下文的上下游关系
  5. 翻译为代码。一个上下文一个module

DDD的基本概念

  • 领域、子域
  • 界限上下文、界限上下文映射
  • 实体、值对象
  • 聚合、聚合根
  • 领域服务
  • 领域事件
  • 工厂
  • 资源库

领域、子域

在领域部不断划分的过程中,领域会细分成不同的子域:核心域、通用域、支撑域

核心域如何区分?桃树例子。界限上下文关心的就是核心域

界限上下文

什么是界限上下文?
例子:我有 kuai di
①我有块地,祖上留下来的
②我有快递,顺丰的

帮助我们理解对话含义的语气和语境就是上下文

例子:

  1. 引用 Eric Evans 对界限上下文的解释(细胞-上下文,细胞膜-边界
  2. 我—>业务流程:乘客 | 宾客 | 支付者 | 咨询师

领域是问题域(即问题空间),界限上下文是问题的解决空间
界限上下文是直译术语,晦涩难懂,理解成本高。应该叫上下文边界

上下文映射(Context Map)

应该译为上下文图,是描述各个上下文之间的关系的总体视图。分别是:

  • 合作关系
  • 共享内核(如:多个服务共享jar包,或者公共基础服务
  • 客户/供应商(如:服务间相互约定模型,由供应方维护
  • 追随者(如:调用支付宝接口,无法约定,只能按照他们协议走
  • 防腐层(如:上游系统模型太烂,下游可以用防腐层来将其转换成理想模型,目的是将上游系统的影响降到最低
  • 公开主机服务(如:对外发布公共服务及接口文档
  • 发布语言
  • 各行其道
  • 大泥球( 毫无设计可言

实体

有唯一标识(可理解为其id),实现了领域行为(充血模型),对象的延续性和标识会跨越甚至超出软件的生命周期。

一个典型的实体应具备三要素:

  • 身份标识
  • 属性
  • 领域行为

值对象(= 值 + 对象)

是否拥有唯一标识,是实体和值对象的根本区别
值对象特征:

  • 不可变,只读,安全
  • 将不同的相关属性组合成了一个概念整体
  • 可以和其他值对象进行相等性比较
  • 行为不会对属性产生副作用

实体和值对象的区别

在实践中,实体和值对象是一起使用的,值对象作为实体的附属属性。如:领域模型中的人员是实体 (有唯一身份标识),而地址对象被人员实体引用。

值对象可以在不同的场景中被不同的实体引用。如在电商系统中,地址对象作为收货地址;在员工系统中,地址对象作为家庭地址。

此外,在某些场景中,地址会占据领域的主导地位,如行政区划中的地址信息维护,这时应该设计为实体。

实体 值对象
具有生命周期 起描述作用
有唯一标识
通过id判断相等性 实现equals方法
增删改查/持久化 即时创建用完就扔
可变 不可变
如 Order/Car Address/Color

聚合、聚合根

在DDD中,实体、值对象是基础的领域对象,表现出的是个体的能力。
而让实体、值对象协同工作的组织,就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性

聚合定义了一组具有内聚关系的相关对象的集合。可以把聚合看做是一个修改数据的单元。

如果一个聚合只有一个实体,那么这个实体就是聚合根;如果有多个实体,那么需要思考聚合内哪个对象有独立存在的意义,并且可以和外部直接进行交互,以其为聚合根

聚合根是与其他聚合交互的唯一接口。

聚合设计原则:

  • 设计小聚合,79%的聚合通常只有一个实体,即聚合根
  • 聚合之间通过Id关联,而不是对象引用
  • 一个事务只能创建或更新一个聚合。这是理想情况,遵循最终一致性。大多数情况我们需要的是事务一致性。

可打破原则的理由:

  • 方便用户界面
  • 缺乏技术支持
  • 全局事务
  • 查询性能