领域驱动设计(DDD)概念入门

936 阅读5分钟

模型是用来解决特定的问题,一般我们只讲“模型对于这个领域是否更有用”,而不是那个模型更好。

领域设计中战略设计

  • 通用语言:用一种语言来清晰的阐述从领域专家的讨论到代码的各个问题和他们的解决方式,但是问题有许多,每一种问题都有各自的通用语言,因此希望在软件的实现上,通过一个边界来使得边界内仅有一种语言
  • 上下文:用来确定单词或者语句含义的设置
  • 限界上下文:语言都有自身的上下文,我们希望管理好每个上下文的秩序,以免混乱,因此引入限界上下文,来帮助我们在实际项目中制作优雅的模型
  • 领域:知识(认知、业务)或者活动范围
  • 模型:领域选定视角的系统上的抽象
  • 上下文映射:多个限界上下文之间存在或多或少的联系,若要知晓全局视图,可以通过恰当的方式来描述他们之间的关联点,明确所有上下文通信所需要的转换,来描绘出当前的情况

假设要做一个在线卖东西的系统,基本需要一个线上卖东西的电商系统、保证商品供应的供应链,以及加上一个数据分析系统,用来预测供给。那么可以做出如下的上下文映射:

image.png

图中每一个限界上下文,都是针对特定领域的一个解决方案,我们可以实现各自的软件来解决对应的问题,领域本身则是描述的业务

从这个角度,我们可以把领域分成问题空间和解决方案空间,在问题空间中,我们思考的是业务所面临的挑战,解决方案空间思考的则是如何实现软件以解决这些挑战

细看电子商务,又可以找到订单、商品、支付、活动等业务,这些都可以称作电商领域的子域

image.png

  • 核心域:业务成功的主要因素,业务命门
  • 支撑子域:专注于业务的某个方面
  • 通用子域:作用于整个业务系统

从全局的角度看,不同的限界上下文存在着千丝万缕的联系,他们之间互相通信,但我们不希望不同领域的知识”泄露“到另外的领域,带来更多的认知成本

image.png

  • 开放主机/消息:定义一种协议,方便不同限界之间通信
  • 防腐层:不同模型之间进行转义

防腐层的作用可以通过如下视图来感受,当你从外面进入房子内部的时候,有一个换鞋的地方,这个地方就可以看做是防腐层 image.png

领域设计中的战术实现

  • 实体:拥有唯一标识,通过它来区分不同的实体。它包含了属性,和属性的行为
  • 值对象:度量或描述领域中某件东西的一个概念,它的所有属性形成一个概念总体,并且值是不可变的
  • 领域服务:领域中的某个操作过程或者转换过程不是实体或值对象的职责,此时将操作过程放到一个单独的接口,即领域服务
  • 领域事件:其它领域关心的发生在当前领域的事件
  • 聚合:一组相关对象的集合,它是数据的修改单元,有自己的聚合根和边界,边界与事务的边界一致,即一个事务只修改一个聚合实例,边界外则一般考虑最终一致性
  • 模块:和领域的概念保持一致,使用通用语言命名,用于组织内聚在一起的领域对象,内聚不强或者没有内聚的领域对象放在不同的模块
  • 工厂:封装所有复杂装配操作的接口
  • 资源库:全局访问,封装实际的存储和查询行为,只为确实需要直接访问的聚合提供资源库,让客户能聚焦于模型

分层模型中使用领域驱动设计

领域驱动设计不需要使用特定的架构,它可以应用于多种架构中,以分层模型为例,一个应用程序可以分成:

  • 用户界面层:处理用户显示和用户请求,不包含任何领域和业务逻辑
  • 应用层:很薄的一层,主要用于对领域对象的协调操作,如果使用ACID数据库,它还负责控制事务保证提交的原子性
  • 领域层:处理业务逻辑,并发布领域事件
  • 基础设施层:持久化、消息通信机制等可以重用的基础设施

如何将领域对象渲染到用户界面显示?如何将用户操作反应到领域模型?

  1. DTO:数据传输对象(Data Transfer Object),包含所有显示的所有属性,缺陷是可能需要创建和领域对象很相似的类,完全耦合了领域对象和界面展示
  2. DPO:领域负载对象(Data Payload Object),包含所有聚合的引用,展现组件通过DPO获取聚合引用,然后从聚合中访问需要的属性
  3. 展示模型:根据状态做出决策,而不是与聚合一一对应,从而使得状态变更与决策放在展示层,与视图分开,比如某个组件是否可编辑可用true表示可以编辑,而false不可编辑,但是true/false的取值却是有展现层进行赋值

参考

Eric Evans演讲what is DDD?
Eric Evans演讲bounded context

Eric Evans 书 <领域驱动设计-软件核心复杂性应对之道>

Vaughn Vernon 书 <实现领域驱动设计>
Martin Fowler 展现模型