领域驱动设计
领域驱动设计实践
一些事实
常见软件状况
- 软件在第一版设计质量最高, 随着需求的变更, 打乱了原有设计, 软件质量会逐渐退化
- 代码变更了, 但是模型没有更新, 越积越多, 后续维护会退缩
常见的误解
- 一套模型打天下: 因为对业务的理解误差, 领域模型是随时都会发生变化的, 模型变化了,代码也是要变化的
- DDD 和现有框架的冲突: DDD 是设计思想, 框架是编程工具, 两者并不冲突,关键是要让人读懂代码,看懂模型
对领域的拆分和实践 (战略层面)
先给领域起个名:
- 用一个名词概括一个领域, 比如 身份认证 就要比 登录 好的多,同时包括了多种业务的可能性
- 这个名词要偏向于业务, 便于沟通和理解
- 不能偏向于业务的某一种可能性, 可能会僵化部分人对业务的理解。 比如 提到 登录, 大部分脑海里第一时间想到两个框框, 一个填用户名,一个填密码 但是提到 身份认证, 部分人除了想到上面的以外,还会想到,验证码登录,刷脸,指纹识别等内容
限界上下文
- 减少误解:能穿多少穿多少 这句话在冬天和夏天 有着不同的理解
- 如果对一个领域中的实体,值对象,领域事件等 的理解没有歧义,限界上下文也就清晰了
大胆使用值对象
- 是对象是可以没有ID 的, 是不可变的实例, 比如 在清空购物车时,我们只关心,你是否提填写了收货地址, 但是不关心你 的地址中的 街道、门牌号等
所以 在订单表中 我们完全可以大胆的设计成
address json NOT NULL
, 在关系表中,序列化成json 字符串存储。 - 而在派送系统中, 却是要关系收货地址 的小区、街道、门牌号,方便安排一次派送任务 尽可能多地派送货物
- 因此, 值对象是基于领域上下文的,
- 但是在代码中,address 还是需要 设计成对象,以满足不同的业务场景和技术需求
实体
- 关于贫血模型和充血模型的争议: 充血模型固然能够聚焦领域,但是现有的技术框架实现不友好; 贫血模型固然是一个数据容器,但对ORM 友好
- 建议: 按框架友好的方式去写代码(战术层面),按模型友好的方式去组织代码(战略层面)
领域服务
- 领域服务可能不属于任何领域,但是它却处理和编排了一系列 领域对象
- 领域服务操作了整个领域对象
应用服务
- 不涉及核心的领域逻辑,但又是不可缺少 的服务,比如发短信, 日志等需求和业务之间的桥接方法
对领域的实践 (战术层面)
前提
- 能够在自己负责的业务系统中, 尽可能准确的识别领域对象
没有最佳实践,只有最适合的实践
-
按照现有的技术框架或者范式, 的确不太容易去按照DDD 的思想去做一些东西,这需要我们大胆尝试和对旧有的范式的打破。
-
按照现有的框架设计, 我们更多的是习惯去写一些JavaBean, 作为数据容器使用。 但是按照充血模型的定义,我们又希望JavaBean 不仅仅是
Getter/Setter
。 所以, 定义一个包,既写JavaBean类,也写 领域方法类, 把包看作一个充血模型的实现, 而不是一个简单的类。 -
多去实践一些 设计模式 和 语言特性,这些 更加‘接地气’,更容易落地到代码上。