仓储查询同一ID时是否应始终返回相同内存引用?
关于仓储返回实体引用的设计思考
这是个非常关键的问题,刚好触及到仓储模式中**身份映射(Identity Map)和工作单元(Unit of Work)**的核心设计逻辑!咱们拆解来看:
先看你的两个断言
assertTrue( a == b ):这个断言不一定成立,完全取决于你的仓储是否实现了Identity Map机制assertTrue( a.equals( b ) ):这个断言必须始终成立,因为a和b代表的是同一个领域实体,它们的业务身份(ID)完全一致,equals方法的实现应该基于业务主键而非内存引用
两种常见的仓储设计逻辑
1. 实现了Identity Map的仓储(推荐)
如果你的仓储遵循DDD中典型的工作单元模式,那么同一仓储实例(或同一工作单元上下文内)多次调用GetById(1),会返回同一个内存引用。
这完全贴合“仓储视为内存中的领域对象集合”的隐喻——就像你从一个内存List里反复取同一个索引的元素,拿到的自然是同一个对象。这种设计的好处很明显:
- 避免内存中存在同一实体的多个副本,防止状态不一致(比如你修改了
a的属性,b会自动同步,因为本质是同一个对象) - 减少不必要的数据库查询(第一次查库后缓存到内存,后续直接返回缓存的引用)
- 保证领域对象的状态一致性,符合DDD中“实体是具有唯一身份的可变对象”的定义
举个实际例子:Entity Framework Core的DbContext就内置了这个机制,同一个DbContext实例中查询同一ID的实体,返回的永远是同一个内存引用。
2. 未实现Identity Map的仓储
这种情况下,每次调用GetById(1)都会返回状态相同但全新的实体实例。
这种设计一般出现在无状态的仓储场景中(比如每个请求都新建仓储实例,且不需要跟踪实体状态),但缺点也很突出:
- 修改其中一个实例的属性不会影响另一个,可能导致后续保存数据时出现冲突或业务逻辑混乱
- 违背了“仓储是内存对象集合”的隐喻,更像是单纯的数据库查询封装
- 重复查询会增加数据库压力(没有内存缓存)
最佳实践建议
如果你在DDD架构下使用仓储模式,强烈建议实现Identity Map机制,这是工作单元模式的核心组成部分,能有效保证内存中领域对象的一致性,也更贴合仓储模式的设计初衷。
另外,务必确保你的实体类equals方法是基于业务主键(比如ID)实现的,而不是依赖默认的引用相等——这样不管仓储是否返回同一引用,只要是同一个实体,equals都会返回true,这是领域实体的基本要求。
内容的提问来源于stack exchange,提问作者Logitek Dev
相关产品推荐
相关产品推荐

