引言
如何应对复杂软件开发?以unix为例
- 封装与抽象
- 分层与模块化
- 基于接口通信
- 为扩展而设计
linux运作漫画图)理论太干,辅以图片说明,同时让听众思考buffer
DDD介绍
- 历史
- 简介
- 一种思维模式
- 本质是方法论
- by experience
由数据模型驱动设计 引入
一门语言的基本语法和编程技巧、一个ORM框架的使用方法及基本的sql编写能力——就这三板斧,足以!
这种设计方式的弊端:mvc模式
- 缺乏边界
- 贫血模型,transactional scripts
- 由贫血症导致的失忆症(即业务代码意图不明
- 跟数据库表定义强耦合
- 缺乏对领域模型的思考和建模
正式引入DDD
怎么玩?
- 团队+领域专家讨论,统一语言
- 战略设计:划分界限上下文
- 战术设计:界限上下文落地
- 根据实际情况迭代、重构界限上下文,往复循环
对比数据模型驱动模式
虚拟钱包案例
mvc模型
- 贫血模型,不够面向对象
- 业务类命名太泛,缺乏边界,职责模糊,后续很容易被腐化
- 数据库表模型散落到业务层,灵活性差
ddd建模
- 充血模型,更加面向对象
- 使用门面模式,屏蔽数据库操作细节
- service层变逻辑变简单,领域逻辑交给领域层做,service层只负责编排
使用原则
- ddd非银弹,不应滥用
- 设计系统时,多考虑墨菲定律
- 划分系统时,多考虑康威定律
总结
- 数据驱动设计有诸多弊端
- DDD天生为控制大型软件的复杂度而生
领域建模流程
- 事件风暴。一起头脑风暴,完善用例图
- 抽象。提炼出系统的主要行为
- 划分界限上下文。主要区分好核心域、支撑域、公共域
- 界限上下文映射。主要区分好各个上下文的上下游关系
- 翻译为代码。一个上下文一个module
DDD的基本概念
- 领域、子域
- 界限上下文、界限上下文映射
- 实体、值对象
- 聚合、聚合根
- 领域服务
- 领域事件
- 工厂
- 资源库
领域、子域
在领域部不断划分的过程中,领域会细分成不同的子域:核心域、通用域、支撑域
核心域如何区分?桃树例子。界限上下文关心的就是核心域
界限上下文
什么是界限上下文?
例子:我有 kuai di
①我有块地,祖上留下来的
②我有快递,顺丰的
帮助我们理解对话含义的语气和语境就是上下文
例子:
- 引用 Eric Evans 对界限上下文的解释(细胞-上下文,细胞膜-边界
- 我—>业务流程:乘客 | 宾客 | 支付者 | 咨询师
领域是问题域(即问题空间),界限上下文是问题的解决空间
界限上下文是直译术语,晦涩难懂,理解成本高。应该叫上下文边界
上下文映射(Context Map)
应该译为上下文图,是描述各个上下文之间的关系的总体视图。分别是:
- 合作关系
- 共享内核(如:多个服务共享jar包,或者公共基础服务
- 客户/供应商(如:服务间相互约定模型,由供应方维护
- 追随者(如:调用支付宝接口,无法约定,只能按照他们协议走
- 防腐层(如:上游系统模型太烂,下游可以用防腐层来将其转换成理想模型,目的是将上游系统的影响降到最低
- 公开主机服务(如:对外发布公共服务及接口文档
- 发布语言
- 各行其道
- 大泥球( 毫无设计可言
实体
有唯一标识(可理解为其id),实现了领域行为(充血模型),对象的延续性和标识会跨越甚至超出软件的生命周期。
一个典型的实体应具备三要素:
- 身份标识
- 属性
- 领域行为
值对象(= 值 + 对象)
是否拥有唯一标识,是实体和值对象的根本区别。
值对象特征:
- 不可变,只读,安全
- 将不同的相关属性组合成了一个概念整体
- 可以和其他值对象进行相等性比较
- 行为不会对属性产生副作用
实体和值对象的区别
在实践中,实体和值对象是一起使用的,值对象作为实体的附属属性。如:领域模型中的人员是实体 (有唯一身份标识),而地址对象被人员实体引用。
值对象可以在不同的场景中被不同的实体引用。如在电商系统中,地址对象作为收货地址;在员工系统中,地址对象作为家庭地址。
此外,在某些场景中,地址会占据领域的主导地位,如行政区划中的地址信息维护,这时应该设计为实体。
实体 | 值对象 |
---|---|
具有生命周期 | 起描述作用 |
有唯一标识 | 无 |
通过id判断相等性 | 实现equals方法 |
增删改查/持久化 | 即时创建用完就扔 |
可变 | 不可变 |
如 Order/Car | Address/Color |
聚合、聚合根
在DDD中,实体、值对象是基础的领域对象,表现出的是个体的能力。
而让实体、值对象协同工作的组织,就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性
聚合定义了一组具有内聚关系的相关对象的集合。可以把聚合看做是一个修改数据的单元。
如果一个聚合只有一个实体,那么这个实体就是聚合根;如果有多个实体,那么需要思考聚合内哪个对象有独立存在的意义,并且可以和外部直接进行交互,以其为聚合根
聚合根是与其他聚合交互的唯一接口。
聚合设计原则:
- 设计小聚合,79%的聚合通常只有一个实体,即聚合根
- 聚合之间通过Id关联,而不是对象引用
- 一个事务只能创建或更新一个聚合。这是理想情况,遵循最终一致性。大多数情况我们需要的是事务一致性。
可打破原则的理由:
- 方便用户界面
- 缺乏技术支持
- 全局事务
- 查询性能