如何创建领域模型并和分析模型绑定,DDD使用了包含若干构造块的模式的语言,来实现领域模型,这是一种采用面向对象的方式,是第5章里面提到领域模型实现方式的一种,其他的方式可以按实际情况采用。

上图展示了领域模型中的许多概念和它们的关联关系,下面是详细介绍。
1. 领域建模相关
下面概念表示了问题域的逻辑,表述了模型对象的关联关系。
- 实体:表示领域中的概念,由身份而不是特性来定义,是业务中承担行为的对象,比如产品、订单。
[图片上传中...(image.png-a82323-1660748013800-0)]
- 具有唯一标识
- 提前生成:唯一标识工具。ice
- 持久化机制生成:数据库自增id。
- 要点
- 构造方法完成唯一标识设定,并且以后不能被修改
- 属性赋值验证
- 属性:单个属性调用set方法的时候直接验证,通过才能赋值。
- 组合验证:给实体增加validate方法,或者专门建立validate类验证。
- 跟踪变化:领域事件和事件存储、发布订阅模式。订单状态的变化。
这里提到用一个实体定义一个基类,提供默认的构造方法、相等比较方法(比较id)、hashcode方法,其他的实体集成并重载部分方法。
- 值对象:用于描述领域实体的特性,具有不变性,没有唯一身份标识、
案例:『数字』,『日期』,『邮寄地址』,这是用于度量或者描述领域中某件东西的概念。比如年龄(出生了多少年)、名字(称呼)。

-
特性
-
不变性:构造函数完成值对象创建,一旦创建后属性不可改变。
-
概念整体:值对象描述整体的概念
Class ThingOfWorth { private string name; private int money; private string currency; } //改进后 Class ThingOfWorth { private string name; private MoneyValue worth;//资产 } Class MoneyValue { private int money; private CURRENCY currency; }
-
可替换性:实体对象对值对象的替换是简单的。
- 简单类型 entity.total = 4; 改成entity.total = 3;
- 复杂类型
entity.value = value1; Value v2 = new Value(2,3); entity.value = v2;
-
相等性:自定义equals方法对比。
-
无副作用:值对象对外提供的方法不改改变值,相当于问值对象问题,这样的表达方式更具有表达性。
Class ExamResult { private math; private english; public function getAmount() { return this->math + this->english } }
-
非常简单的类型不需要封装为值对象,布尔,数值类型。
-
值对象引用实体
//businessPriority 值对象 //product 实体对象 float priority = businessPriority.priorityOf(product); 改进后 float priority = businessPriority.priority( product.businessPriorityTotals());
- 值对象尽量只理解自身的属性和状态。
- 第一段代码并不知道使用了Product哪些部分,表达不清晰。
- 不能看出是否会对product的属性进行修改。
-
值对象实现(构造方法)
- 有参数和无参数的(根据配置给属性赋值的)
- 根据另一个值对象复制(可能没有必要)
-
持久化值对象:(不是存入数据库的就必须是实体),有时值对象需要以实体的身份持久化。
-
值对象和实体的选择(描述,是,是,不是的回答用值对象)
是领域的一个东西,还是只是描述或者度量东西?
如果是描述,能否满足值对象的特征?
将该概念建模成实体是不是只是持久化机制考虑?
如果建模成实体是不是因为拥有唯一标识,需要关注实体的个体性,并需要在声明周期跟踪变化。?
- 领域服务
表示一个无状态的操作,用于实现某个特定的领域任务,某些操作不适合放在聚合和值对象上就用领域服务,比如计价、身份认证。
过度使用领域服务导致领域贫血模型:表现现象为所有的业务逻辑都在领域服务中,而不是实体和值对象中
- 适用场景
- 执行一个显著的业务操作过程。
- 对领域对象进行转换。
- 以多个领域对象作为输入进行计算,结果产生值对象。
4.模块

通过模块(包或者命名空间实现)将领域模型的不同领域对象分组,实现高内聚低耦合,让不同部分可以被单独理解。
2.生命周期模式
1.聚合

业务中实体和值对象组合用于描述领域模型中复杂的关联关系,比如上图,我们很难同时保持上述复杂关联关系的并发性和一致性,比如我们不能仅仅因为一个较早的订单状态发生变化,就阻止用户更新账户的地址,因为这两个行为不相关,没有必要保持并发和一致性,我们可以把不同实体按照相关性和关联性划分成聚合,每次都通过聚合根修改数据,多个聚合之间用聚合根id引用,而不能直接引用聚合的数据。

比如两个人同时要修改同一个产品的颜色和尺寸,产品是聚合根,这里要保证并发和一致性,不能丢失数据,可以提前对产品id加锁的方式解决,也可以在更新数据的时候通过数据库的乐观锁形式比如update xx where id = 1 and color = before_color这样的断言,更新行数为0提示更新失败。
聚合的划分不能仅仅通过入口,通过入口划分后要考虑不变条件

这里再次划分成为电子钱包
和地址簿聚合
,这样修改地址和信用卡可以同时进行,修改信用卡比如通过电子钱包
这个聚合根实现。
在举一个例子:申请单和申请明细条目,比如创建了一个申请单,在申请单状态是等待审核之前或者被驳回时是可以修改申请内容的,正在审核中或者审核完成是不能修改的,这里修改申请明细就需要通过申请单这个聚合根来修改。
聚合必须总是处于一致性状态,相当于原子单元、聚合根会充当入口点,聚合外部的对象不能保留对聚合内部对象的引用
2.工厂
对象或者值对象创建比较复杂时,应该使用工厂,工厂确保对象创建之前满足所有的不变性条件,不复杂的时候使用简单的构造器方法。
3.存储库
聚合内部是原子操作保持一致性,需要将聚合变更的数据持久化,聚合和存储库用ORM框架可以关联做到数据存储和读取。

3.显露模式
1.领域事件
当聚合或者实体发生变化的时候,可以将事件发布,其它方可以根据自己需要监听,比如购物车发生加购或者减购的时候要为用户推荐不同的菜,加减商品就可以作为领域事件发布,推荐系统去监听。领域事件也可以作为聚合之间通信的手段。
2.事件溯源
比如对于订单状态来说,订单状态已创建、已付款、已发货、配送中,可以将每次订单变化的事件都记录下来,有利于做分析溯源,比如订单为什么减少了?
网友评论