2. 领域驱动设计核心概念回顾:实体、值对象、聚合、领域事件、限界上下文

在定义缓存与数据库一致性边界之前,必须统一团队对领域驱动设计(DDD)核心战术构件与战略构件的理解。这些概念不仅是建模语言,更是决定缓存粒度、失效策略与一致性协议的直接依据。本节以“一致性边界”为视角,重新审视五个核心概念。

2.1 实体(Entity)—— 具有唯一标识与连续生命周期的对象

实体由 标识(Identity) 而非属性定义。即使所有属性都发生变化,只要标识不变,实体仍然是同一个对象。实体的状态变化需要被持久化,且通常需要事务性保护。

特征 说明 缓存影响
唯一标识 ID、UUID、业务主键 缓存键通常直接使用实体ID
可变状态 属性可被修改,状态随时间变化 缓存必须支持失效或更新
生命周期 创建、修改、删除、归档 缓存策略需覆盖全生命周期事件
相等性 通过标识比较,而非属性 缓存命中判断依赖标识匹配
一致性边界启示: 实体是缓存一致性保护的最小可寻址单元。对实体的任何写操作都必须触发对应缓存键的失效或同步更新。

2.2 值对象(Value Object)—— 描述性、不可变、无标识

值对象通过属性值定义自身,两个值对象如果所有属性相同,则视为相等。值对象一旦创建就不应被修改,修改应通过替换整个对象实现。值对象通常被实体或聚合引用。

  • 不可变性: 值对象创建后状态不可变,天然适合缓存且无需失效。
  • 无标识: 缓存键由值对象的所有属性组合生成(如哈希键)。
  • 共享安全: 多个实体可安全共享同一个值对象实例,不会产生副作用。
缓存优化建议: 值对象可以长期缓存,甚至使用本地堆内缓存,因为不存在写冲突。当值对象依赖的外部数据源发生变化时,通过版本号或时间戳整体替换。

2.3 聚合(Aggregate)—— 一致性边界的基本单元

聚合是领域驱动设计中最重要的战术模式,它定义了一组相关对象的 事务一致性边界。聚合根(Aggregate Root)是外部访问聚合的唯一入口。聚合内部的所有对象在同一个事务中保持一致。

聚合:一致性边界示意图 聚合边界(事务一致性边界) 聚合根:Order 实体:OrderItem 值对象:Address 值对象:Money 外部客户端 只能通过聚合根访问
缓存一致性核心原则: 聚合是缓存失效的最小原子单位。对聚合内任何对象的修改,都应导致整个聚合的缓存失效或整体刷新。绝不允许部分更新聚合内缓存,否则将产生脏读。

2.4 领域事件(Domain Event)—— 跨聚合的异步通知机制

领域事件记录聚合内发生的、对业务有意义的过去时态事实。事件用于在聚合之间传递状态变更通知,而不破坏聚合的事务边界。

属性 示例 缓存用途
事件类型 OrderPlaced 区分缓存失效策略
事件时间 2025-01-15T10:30:00Z 判断缓存时效性
聚合ID order-12345 定位需要失效的缓存键
事件数据 订单金额、商品列表 用于缓存预热或写回
缓存一致性边界启示: 领域事件是缓存失效的天然触发器。当聚合A发布事件时,订阅该事件的聚合B、C的缓存应被异步失效或更新。事件驱动缓存一致性是最终一致性的典型实现。

2.5 限界上下文(Bounded Context)—— 模型与缓存的战略边界

限界上下文是领域驱动设计的战略核心,它定义了模型、语言和实现边界。每个限界上下文拥有独立的领域模型、独立的数据库(或Schema)、独立的缓存空间。

限界上下文与缓存边界 订单上下文 • 模型:Order, OrderItem • 数据库:order_db • 缓存:Redis 实例 A • 缓存键前缀:order: • 一致性:强一致性 库存上下文 • 模型:Product, Stock • 数据库:inventory_db • 缓存:Redis 实例 B • 缓存键前缀:stock: • 一致性:最终一致性 支付上下文 • 模型:Payment, Transaction • 数据库:payment_db • 缓存:Redis 实例 C • 缓存键前缀:pay: • 一致性:最终一致性 领域事件总线(异步缓存失效通知)
缓存边界定义方法: 每个限界上下文应拥有独立的缓存实例或缓存命名空间。缓存键设计应包含上下文标识前缀,避免键冲突。上下文之间的缓存一致性通过领域事件驱动,而非共享缓存。

2.6 核心概念对比总结

概念 标识 可变性 缓存粒度 一致性要求 失效触发
实体 有 可变 单键 强一致性 写操作
值对象 无 不可变 组合键 无需失效 版本变更
聚合 聚合根标识 整体一致 聚合根键 事务强一致 聚合内任何变更
领域事件 事件ID 不可变 事件流 最终一致 事件发布
限界上下文 上下文名 独立演进 缓存实例 上下文内强一致 上下文边界
常见误区: 不要将值对象与实体混用缓存策略;不要在聚合外部直接缓存聚合内部对象;不要跨限界上下文共享缓存实例。这些做法会破坏一致性边界,导致难以排查的脏读和缓存穿透问题。