我认为术语“存储库”通常被认为是在Martin Fowler的“企业应用程序架构模式”一书中描述的“存储库模式”。
存储库在域和数据映射层之间进行调解,就像内存中的域对象集合一样。客户端对象以声明方式构造查询规范,并将它们提交给 Repository 以获得满意。对象可以添加到存储库中,也可以从存储库中删除,就像它们可以从简单的对象集合中一样,存储库封装的映射代码将在幕后执行适当的操作。
从表面上看,Entity Framework 完成了所有这些,并且可以用作存储库的简单形式。但是,存储库可能不仅仅是数据层抽象。
根据 Eric Evans 的《领域驱动设计》一书,存储库具有以下优点:
- 他们为客户提供了一个简单的模型,用于获取持久性对象并管理它们的生命周期
- 它们将应用程序和域设计与持久性技术、多种数据库策略甚至多个数据源解耦
- 他们传达有关对象访问的设计决策
- 它们允许轻松替换虚拟实现以进行单元测试(通常使用内存中的集合)。
第一点大致相当于上面那一段,不难看出Entity Framework本身很容易做到。
有人会争辩说 EF 也完成了第二点。但通常 EF 仅用于将每个数据库表转换为 EF 实体,并将其传递给 UI。它可能抽象了数据访问的机制,但几乎没有抽象出幕后的关系数据结构。
在主要面向数据的简单应用程序中,这似乎不是重点。但是随着应用程序的领域规则/业务逻辑变得更加复杂,您可能希望更加面向对象。数据的关系结构包含对业务领域不重要的特性,但这些特性是数据存储的副作用,这种情况并不少见。在这种情况下,仅仅抽象持久性机制是不够的,还要抽象数据结构本身的性质。单独的 EF 通常不会帮助您做到这一点,但存储库层会。
至于第三个优势,EF 不会(从 DDD 的角度)提供帮助。通常 DDD 不仅使用存储库来抽象数据持久性的机制,而且还提供有关如何访问某些数据的约束:
我们也不需要对更方便通过遍历查找的持久对象进行查询访问。例如,可以从 Person 对象请求一个人的地址。最重要的是,禁止访问 AGGREGATE 内部的任何对象,除非从根遍历。
换句话说,您不会仅仅因为您的数据库中有一个地址表而有一个“AddressRepository”。如果您的设计选择以这种方式管理 Address 对象的访问方式,那么 PersonRepository 就是您定义和实施设计选择的地方。
此外,DDD 存储库通常是封装与域数据集相关的某些业务概念的地方。OrderRepository 可能有一个名为OutstandingOrdersForAccount 的方法,它返回一个特定的Orders 子集。或者,客户存储库可能包含 PreferredCustomerByPostalCode 方法。
如果没有添加存储库抽象层,Entity Framework 的 DataContext 类不能很好地适应这种功能。它们适用于 DDD 所谓的规范,它可以是简单的布尔表达式,发送到一个简单的方法,该方法将根据表达式评估数据并返回匹配项。
至于第四个优势,虽然我确信有某些策略可能会让一个人替代数据上下文,但将其包装在存储库中会使它变得非常简单。
关于“工作单元”,这是 DDD 书中所说的:
将事务控制权留给客户端。虽然 REPOSITORY 会插入和删除数据库,但它通常不会提交任何内容。例如,在保存后提交是很诱人的,但客户端可能具有正确启动和提交工作单元的上下文。如果 REPOSITORY 不动手,事务管理会更简单。