Yujun's Blog
DDD(二):深入战术设计:掌握领域模型的“基石
DDD入门(二):深入战术设计:掌握领域模型的“基石”
前面我们已经学习战略设计的一个思想,接下来我们可以深入进某个领域内进行具体的战术设计。
如果说战略设计帮我们画好了“作战地图”,区分了不同的“战区”(限界上下文),那么战术设计就是指导我们如何在每一个具体的“战区”内部,精心构建我们的“防御工事”和“作战单位”(也就是我们的领域模型)。它提供了一套丰富的工具和模式,让我们能够设计出既能精确反映业务逻辑,又健壮、灵活的模型。
那么,构建这些模型最基础的“砖瓦”或者说“零件”是什么呢?
模型的基础单元:实体与值对象
就像玩乐高需要基础的积木块一样,构建领域模型也需要最基本的构建单元。在 DDD 的战术设计中,这两种最基础、最核心的单元就是 实体(Entity) 和 值对象(Value Object)。理解它们的区别和用途,是进行精细化模型设计的起点。
实体 - “有身份证的东西”
想象一下我们现实世界中的你、我、他,或者一张特定的订单、一件特定的商品。它们都有一个共同点:拥有唯一的身份标识,并且在生命周期中,它们的属性可能会改变,但它们的身份是持续不变的。这就是“实体”。核心特征如下:
- 唯一身份标识 (Identity): 每个实体都有一个全局唯一的 ID(比如用户 ID、订单号、身份证号),用于区分其他实体,即使其他所有属性都一样。
- 连续性/生命周期 (Continuity/Lifecycle): 实体有创建、修改、可能最终被归档或删除的生命周期。状态会随时间变化。
- 可变性 (Mutability): 实体的属性(非 ID)通常是可变的。比如用户的地址可以修改,订单的状态会从“待支付”变成“已支付”。
例如:
- 用户 (User): 由 用户ID 标识。姓名、地址、电话都可能改变,但只要 用户ID 不变,就还是那个用户。
- 商品 (Product): 由 商品SKU 或 商品ID 标识。商品名称、描述、价格可能会更新。
- 订单 (Order): 由 订单号 标识。订单状态、包含的商品、收货地址可能在处理过程中变化。
值对象 - “描述性的属性包”
现在想象一下“地址”、“金额”、“颜色”或者一个“日期范围”。这些东西我们通常不关心它们的“身份”,只关心它们所包含的“值”或“属性”。它们用来描述事物的特征,并且通常一旦创建就不应该改变。这就是“值对象”。
-
属性定义身份 (Attribute-defined Identity): 值对象没有单独的 ID。它的“身份”完全由其包含的所有属性的值共同决定。如果两个值对象的所有属性值都相等,那么它们就是等同的。
-
通常不可变 (Immutability Recommended): 强烈建议将值对象设计为不可变的。一旦创建,其内部状态(属性值)就不再改变。如果需要“修改”,实际上是创建一个新的值对象来替换旧的。这样做的好处是:代码更简单、线程安全、可放心共享引用、行为更可预测。
-
概念整体性 (Conceptual Whole): 它将一组紧密相关的属性打包成一个有意义的概念单元。
-
地址 (Address): 包含省、市、街道等属性。两个地址对象,只要这些属性值都一样,就认为它们是同一个地址。我们通常不给地址一个独立的 ID。
当然,仅仅有基础材料还不够,我们还需要知道如何把它们有效地组合起来,构建出更稳固的结构。在下一篇文章中,我们将探讨 DDD 战术设计中另一个极其重要的模式——聚合(Aggregate),看看它是如何将实体和值对象组织在一起,保护我们业务规则和数据一致性的。敬请期待!
参考
本文参考自:《极客时间——DDD实战课》