存储库模式解决什么特定的问题?

(注:我的问题与三个月前提出这个问题的人非常相似,但是没有回答。)

我最近开始使用MVC3 +entity framework,我一直在阅读,最好的做法是使用存储库模式来集中访问DAL。 这也伴随着解释,你想保持DAL独立于领域,特别是视图层。 但在我见过的例子中,知识库是(或似乎是 )简单地返回DAL实体,即在我的情况下,知识库将返回EF实体。

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

此外,我至less在一个地方读到工作单元模式集中存储库访问,以pipe理数据上下文对象,但我不明白为什么这也很重要。

我98.8%肯定我在这里错过了一些东西,但从我的阅读中我没有看到。 当然,我可能只是没有阅读正确的消息来源:

entity framework的DbContext基本上类似于存储库 (以及工作单元 )。 你不一定要在简单的情况下把它抽象出来。

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

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

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

这基本上是在体系结构层面上的面向对象和程序编程。

另请参阅端口和适配器又名六angular形build筑

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

我认为术语“知识库”通常被认为是由Martin Fowler 的“企业应用程序体系结构 模式 ”一书描述的“ 知识库模式 ”。

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

从表面上看,Entity Framework完成了所有这些工作,并且可以用作存储库的简单forms。 但是,对于一个存储库来说,可能不仅仅是一个数据层抽象。

根据埃里克·埃文斯(Eric Evans)的“ 域驱动devise ”( Domain Driven Design)一书,存储库具有以下优点

  • 他们向客户提供一个简单的模型来获得持久性对象并pipe理其生命周期
  • 他们将应用程序和域devise与持久性技术,多个数据库策略甚至多个数据源分离开来
  • 他们传达关于对象访问的devise决定
  • 它们允许简单replace虚拟实现,用于unit testing(通常使用内存中的集合)。

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

有些人会认为EF也完成了第二点。 但通常使用EF来简单地将每个数据库表转换为EF实体,并将其传递给UI。 它可能是抽象的数据访问机制 ,但它很难抽象出背后的关系数据结构。

在更简单的应用程序,主要是数据导向 ,这可能似乎并不重要。 但是,随着应用程序的领域规则/业务逻辑变得越来越复杂,您可能希望更加面向对象 。 数据的关系结构包含对业务领域并不重要的特性并不less见,但却是数据存储的副作用。 在这种情况下,抽象出持久性机制还不够,而且数据结构本身的性质也是不够的。 单靠英孚通常不会帮助你做到这一点,但一个储存库层会。

至于第三个优点,EF将无能为力(从DDD的angular度来看)。 通常,DDD使用存储库不仅仅是为了抽象数据持久性的机制,还要提供如何访问特定数据的限制:

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

换句话说,你不会因为你的数据库中有一个地址表而没有“AddressRepository”。 如果您的deviseselect以这种方式pipe理Address对象的访问方式,那么PersonRepository就是您定义和执行deviseselect的地方。

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

如果没有添加的存储库抽象层,entity framework的DataContext类不适合这种function。 对于DDD调用的规范,它们可以很好地工作,它可以是一个简单的布尔expression式,它被发送到一个简单的方法,根据expression式评估数据并返回一个匹配。

至于第四个优点,虽然我确定有一些策略可以让我们replacedatacontext,但是将它包装到一个仓库中会使其变得简单。

关于“工作单位”,以下是DDD书的内容:

将交易控制留给客户。 虽然REPOSITORY会插入和删除数据库,但通常不会提交任何内容。 举例来说,在保存之后很容易进行提交,但是客户端可能具有正确启动和提交工作单元的上下文。 如果REPOSITORY不动手,交易pipe理将变得更简单。

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

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

还有一种情况是你从多个来源获取信息(db +文件系统),一个回购将作为门面,但它仍然是一个DAO的另一个名字。

只有在处理域/业务对象时,“真正的”存储库才是有效的,对于不会改变存储的以数据为中心的应用程序,ORM本身就足够了。

如果您有多个数据源,并且想要使用一致的编码策略访问这些数据源,那么这将非常有用。

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

您可能不希望业务层或前端层关心数据来自何处,因此您需要构build通用存储库模式来访问“数据”,而不是访问“entity framework数据”。

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

考虑到你的情况,我只需要select一组接口来表示你的数据层需要返回哪些数据结构(你的领域模型)。 您的实现可以是EF,Raw ADO.Net或任何其他types的Data Store / Provider的混合。 这里的关键策略是将实现抽象为直接的消费者 – 您的Domain层。 当你想unit testing你的域对象时,这是非常有用的,而在不常见的情况下 – 完全改变你的数据提供者/数据库平台。

你应该,如果你还没有,考虑使用IOC容器,因为他们通过dependency injection非常容易地松散耦合你的解决scheme。 有很多可用的 ,我个人更喜欢Ninject 。

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

另一个要考虑的事情是,即使你知道你将使用单个数据存储,但是创build一个存储库抽象仍然是有意义的。 原因在于你的应用程序可能有一个函数需要你的ORM du jour要么性能不好,要么根本不知道如何使ORM满足你的需求。

如果您将自己的ORM包装在深思熟虑的存储库界面中,则可以根据需要轻松地在不同的技术之间进行切换。 在我的知识库中看到一些方法使用EF作为工作,而其他方法使用PetaPoco或(gasp)ADO.net代码。 存储库抽象使您能够使用恰当的工具来完成手头的工作,而不会将这些复杂性泄露到客户端代码中。

我认为有很多文章称之为“知识库”,这是一个很大的误解。 这就是为什么人们怀疑这些抽象带来的真正价值。

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

我在这里博客了。