2. 领域驱动设计核心概念回顾:实体、值对象、聚合、领域事件、限界上下文
在定义缓存与数据库一致性边界之前,必须统一团队对领域驱动设计(DDD)核心战术构件与战略构件的理解。这些概念不仅是建模语言,更是决定缓存粒度、失效策略与一致性协议的直接依据。本节以“一致性边界”为视角,重新审视五个核心概念。
2.1 实体(Entity)—— 具有唯一标识与连续生命周期的对象
实体由 标识(Identity) 而非属性定义。即使所有属性都发生变化,只要标识不变,实体仍然是同一个对象。实体的状态变化需要被持久化,且通常需要事务性保护。
| 特征 | 说明 | 缓存影响 |
|---|---|---|
| 唯一标识 | ID、UUID、业务主键 | 缓存键通常直接使用实体ID |
| 可变状态 | 属性可被修改,状态随时间变化 | 缓存必须支持失效或更新 |
| 生命周期 | 创建、修改、删除、归档 | 缓存策略需覆盖全生命周期事件 |
| 相等性 | 通过标识比较,而非属性 | 缓存命中判断依赖标识匹配 |
一致性边界启示: 实体是缓存一致性保护的最小可寻址单元。对实体的任何写操作都必须触发对应缓存键的失效或同步更新。
2.2 值对象(Value Object)—— 描述性、不可变、无标识
值对象通过属性值定义自身,两个值对象如果所有属性相同,则视为相等。值对象一旦创建就不应被修改,修改应通过替换整个对象实现。值对象通常被实体或聚合引用。
- 不可变性: 值对象创建后状态不可变,天然适合缓存且无需失效。
- 无标识: 缓存键由值对象的所有属性组合生成(如哈希键)。
- 共享安全: 多个实体可安全共享同一个值对象实例,不会产生副作用。
缓存优化建议: 值对象可以长期缓存,甚至使用本地堆内缓存,因为不存在写冲突。当值对象依赖的外部数据源发生变化时,通过版本号或时间戳整体替换。
2.3 聚合(Aggregate)—— 一致性边界的基本单元
聚合是领域驱动设计中最重要的战术模式,它定义了一组相关对象的 事务一致性边界。聚合根(Aggregate Root)是外部访问聚合的唯一入口。聚合内部的所有对象在同一个事务中保持一致。
缓存一致性核心原则: 聚合是缓存失效的最小原子单位。对聚合内任何对象的修改,都应导致整个聚合的缓存失效或整体刷新。绝不允许部分更新聚合内缓存,否则将产生脏读。
2.4 领域事件(Domain Event)—— 跨聚合的异步通知机制
领域事件记录聚合内发生的、对业务有意义的过去时态事实。事件用于在聚合之间传递状态变更通知,而不破坏聚合的事务边界。
| 属性 | 示例 | 缓存用途 |
|---|---|---|
| 事件类型 | OrderPlaced | 区分缓存失效策略 |
| 事件时间 | 2025-01-15T10:30:00Z | 判断缓存时效性 |
| 聚合ID | order-12345 | 定位需要失效的缓存键 |
| 事件数据 | 订单金额、商品列表 | 用于缓存预热或写回 |
缓存一致性边界启示: 领域事件是缓存失效的天然触发器。当聚合A发布事件时,订阅该事件的聚合B、C的缓存应被异步失效或更新。事件驱动缓存一致性是最终一致性的典型实现。
2.5 限界上下文(Bounded Context)—— 模型与缓存的战略边界
限界上下文是领域驱动设计的战略核心,它定义了模型、语言和实现边界。每个限界上下文拥有独立的领域模型、独立的数据库(或Schema)、独立的缓存空间。
缓存边界定义方法: 每个限界上下文应拥有独立的缓存实例或缓存命名空间。缓存键设计应包含上下文标识前缀,避免键冲突。上下文之间的缓存一致性通过领域事件驱动,而非共享缓存。
2.6 核心概念对比总结
| 概念 | 标识 | 可变性 | 缓存粒度 | 一致性要求 | 失效触发 |
|---|---|---|---|---|---|
| 实体 | 有 | 可变 | 单键 | 强一致性 | 写操作 |
| 值对象 | 无 | 不可变 | 组合键 | 无需失效 | 版本变更 |
| 聚合 | 聚合根标识 | 整体一致 | 聚合根键 | 事务强一致 | 聚合内任何变更 |
| 领域事件 | 事件ID | 不可变 | 事件流 | 最终一致 | 事件发布 |
| 限界上下文 | 上下文名 | 独立演进 | 缓存实例 | 上下文内强一致 | 上下文边界 |
常见误区: 不要将值对象与实体混用缓存策略;不要在聚合外部直接缓存聚合内部对象;不要跨限界上下文共享缓存实例。这些做法会破坏一致性边界,导致难以排查的脏读和缓存穿透问题。