存储库模式解决了哪些具体问题?

时间:2012-11-01 15:40:01

标签: entity-framework design-patterns domain-driven-design repository-pattern

(注意:我的问题与三个月前问过this question的人的问题非常相似,但从未得到回答。)

我最近开始使用MVC3 + Entity Framework,我一直在阅读最佳实践是使用存储库模式集中访问DAL。这也伴随着您希望将DAL与域(尤其是视图层)分开的解释。但是在示例中,我看到存储库(或似乎是)只是返回DAL实体,即在我的情况下,存储库将返回EF实体。

所以我的问题是,如果只返回DAL实体,那么存储库有什么用呢?这是否会增加一层复杂性,而不会消除在层之间传递DAL实体的问题?如果存储库模式创建“进入DAL的单一入口点”,那么它与上下文对象有何不同?如果存储库提供了一种检索和持久化DAL对象的机制,那么它与上下文对象有何不同?

另外,我至少读过一个地方,工作单元模式集中了存储库访问以管理数据上下文对象,但我不知道为什么这很重要。

我98.8%肯定我在这里遗漏了一些东西,但从我的阅读中我没有看到它。当然,我可能只是没有阅读正确的资料来源......:\

7 个答案:

答案 0 :(得分:50)

我认为术语“知识库”通常被认为是由Martin Fowler所着的书repository pattern描述的“Patterns of Enterprise Application Architecture”。

  

存储库在域和数据映射层之间进行调解,   表现得像内存中的域对象集合。客户对象   以声明方式构造查询规范并将其提交给   存储库是为了满足。可以添加和删除对象   存储库,因为它们可以从一个简单的对象集合,和   由Repository封装的映射代码将执行   幕后的适当操作。

从表面上看,Entity Framework可以完成所有这些工作,并且可以用作存储库的简单形式。但是,存储库可能不仅仅是数据层抽象。

根据Eric Evans的书Domain Driven Design,存储库具有以下优势:

  
      
  • 他们为客户提供了一个简单的模型来获取持久性对象并管理他们的生命周期
  •   
  • 他们将应用程序和域设计与持久性技术,多个数据库策略甚至多个数据源分离
  •   
  • 他们传达有关对象访问的设计决策
  •   
  • 它们允许轻松替换虚拟实现,进行单元测试(通常使用内存中的集合)。
  •   

第一点大致等同于上面的段落,很容易看出实体框架本身很容易实现它。

有人会争辩说EF也会完成第二点。但通常EF仅用于将每个数据库表转换为EF实体,并将其传递给UI。它可能是抽象数据访问的机制,但它几乎没有抽象出幕后的关系数据结构。

在大多数面向数据的简单应用程序中,这似乎不是一个重要的观点。但随着应用程序的域规则/业务逻辑变得更加复杂,您可能希望更多面向对象。数据的关系结构包含对业务域不重要的特性,但是数据存储的副作用并不罕见。在这种情况下,抽象持久性机制以及数据结构本身的性质是不够的。单独的EF通常不会帮助你这样做,但存储库层将会。

至于第三个优势,EF将不做任何事情(从DDD的角度来看)来提供帮助。通常,DDD不仅使用存储库来抽象数据持久性机制,而且还提供有关如何访问某些数据的约束:

  

对于更多的持久对象,我们也不需要查询访问权限   通过遍历方便找到。例如,一个人的地址   可以从Person对象请求。最重要的是,任何   禁止访问AGGREGATE内部的对象,除非   从根遍历。

换句话说,只是因为数据库中有一个Address表,就不会有'AddressRepository'。如果您的设计选择以这种方式管理Address对象的访问方式,那么PersonRepository就是您定义和实施设计选择的地方。

此外,DDD存储库通常是封装与域数据集相关的某些业务概念的位置。 OrderRepository可能有一个名为OutstandingOrdersForAccount的方法,它返回Orders的特定子集。或者Customer存储库可能包含PreferredCustomerByPostalCode方法。

如果没有添加的存储库抽象层,实体框架的DataContext类就无法很好地适应这种功能。它们可以很好地用于DDD调用规范,它可以是简单的布尔表达式,发送到一个简单的方法,它将根据表达式计算数据并返回匹配。

至于第四个优点,虽然我确信某些策略可能让一个人替换datacontext,但将其包装在存储库中会使其变得简单。

关于“工作单位”,这是DDD书中所说的内容:

  

将事务控制留给客户端。虽然REPOSITORY将插入数据库并从数据库中删除,但通常不会   做任何事。例如,在保存后提交是很诱人的   但客户可能有正确发起的背景和   提交工作单位。如果是,交易管理会更简单   REPOSITORY保持双手。

答案 1 :(得分:21)

实体框架的DbContext基本上类似于存储库(以及工作单元)。在简单的场景中,您不一定要抽象它。

存储库的主要优点是您的域可以是无知的并且独立于持久性机制。在基于层的体系结构中,依赖关系从UI层向下指向域(或通常称为业务逻辑层)到数据访问层。这意味着UI取决于BLL,BLL本身取决于DAL。

在更现代的架构中(由域驱动设计和其他面向对象的方法传播),域应该没有向外的依赖关系。这意味着UI,持久性机制和其他一切都应该依赖于域,而不是相反。

然后,存储库将通过其域内的接口表示,但在域外的具体实现中,在持久性模块中。这样,域只依赖于抽象接口,而不是具体的实现。

这基本上是面向对象而不是架构层面的程序编程。

另请参阅Ports and Adapters a.k.a. Hexagonal Architecture

存储库的另一个优点是您可以为各种数据源创建类似的访问机制。不仅适用于数据库,还适用于基于云的商店,外部API,第三方应用程序等。

答案 2 :(得分:6)

你是对的,在这些简单的情况下,存储库只是DAO的另一个名称,它只带来一个值:事实上你可以将EF切换到另一种数据访问技术。今天你正在使用MSSQL,明天你会想要一个云存储。或者使用micro orm而不是EF或从MSSQL切换到MySql。

在所有这些情况下,使用存储库是件好事,因为应用程序的其余部分不会关心您现在使用的存储空间。

还有一个有限的案例,你从多个来源(db +文件系统)获取信息,一个repo将作为外观,但它仍然是DAO的另一个名称。

“真实”存储库仅在您处理域/业务对象时有效,对于不会更改存储的以数据为中心的应用程序,单独使用ORM就足够了。

答案 3 :(得分:2)

在您拥有多个数据源并希望使用一致的编码策略访问它们的情况下,它会很有用。

例如,您可能有多个EF数据模型,一些数据使用传统ADO.NET访问存储过程,一些数据使用第三方API访问,一些数据访问来自Windows NT4服务器上的Access数据库坐在扫帚壁橱里的尘土毯下。

您可能不希望您的业务或前端层关注数据的来源,因此您需要构建通用存储库模式来访问“数据”,而不是访问“实体框架数据”。

在这种情况下,您的实际存储库实现将彼此不同,但调用它们的代码不会知道其中的区别。

答案 4 :(得分:2)

根据您的情况,我会选择一组接口来表示需要从数据层返回哪些数据结构(您的域模型)。然后,您的实现可以是EF,Raw ADO.Net或任何其他类型的数据存储/提供程序的混合。这里的关键策略是实现从直接消费者 - 您的域层 - 抽象出来。当您想要对域对象进行单元测试时,这很有用,在不太常见的情况下,完全更改数据提供者/数据库平台。

如果你还没有,你应该考虑使用IOC container,因为它们可以通过Dependency Injection轻松地解决你的解决方案。 There are many available,我个人更喜欢Ninject

域层应该封装您的所有业务逻辑 - 问题域的规则和要求,并且可以直接由MVC3 Web应用程序使用。在某些情况下,引入位于域层之上的services layer是有意义的,但这并不总是必要的,并且对于简单的Web应用程序来说可能过度。

答案 5 :(得分:1)

另一件需要考虑的事情是,即使您知道自己将使用单个数据存储,创建存储库抽象仍然有意义。原因是您的应用程序可能需要一个函数,您的ORM du jour要么表现不好(性能),要么根本没有,或者您只是不知道如何使ORM屈服于您的需求。

如果您将ORM包装在经过深思熟虑的存储库界面后面,您可以根据需要轻松切换不同的技术。在我的存储库中看到一些方法使用EF进行工作而其他人使用像PetaPoco或(喘气)ADO.net代码这样的东西并不罕见。存储库抽象使您可以使用恰当的工具来完成手头的工作,而不会将这些复杂性泄露到客户端代码中。

答案 6 :(得分:0)

我认为很多文章称之为“存储库”存在很大的误解。这就是为什么人们怀疑这些抽象带来的真正价值。

在我看来,它的纯粹形式的存储库是IEnumerable,而你和很多文章都在讨论“数据访问服务”。

我在博客上发表了here