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

Dav*_*ave 43 design-patterns domain-driven-design entity-framework repository-pattern

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

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

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

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

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

Eri*_*ing 52

我认为术语"存储库"通常被认为是由Martin Fowler 的"企业应用程序架构模式 "一书描述的" 存储库模式 " .

存储库在域和数据映射层之间进行调解,其作用类似于内存中的域对象集合.客户端对象以声明方式构造查询规范,并将它们提交给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保持不动,交易管理将更加简单.

  • @ChrisPratt 嗯,我们有些同意。问题是,换句话说,“我有一个自称为存储库的上下文,但它必须有更多内容,我错过了什么?”。答案是,所有这些其他东西都放在 ORM 之上,使其成为*实际的存储库*。但是当你说“每个`DbSet`都是一个存储库”时,我不得不强烈反对。它只是数据库表的抽象,但这并不能使其成为存储库。 (2认同)

Den*_*aub 22

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

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

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

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

这基本上是面向对象与体系结构上的过程编程.

另请参见端口和适配器(即六边形体系结构).

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

  • @Dave 提供视图的单独上下文不需要存储库(它也不需要域,因为显示数据可能不涉及太多业务逻辑)。只需让 UI 直接使用该 DbContext 来检索视图,可能会按用户、组或其他限制进行过滤。我想推荐在这里查看 **CQRS** 架构风格(只是谷歌这个词)。我在上面扩展了我的答案,试图解释存储库的目的。 (2认同)

Mik*_*eSW 6

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

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

还有一个有限的情况,你从多个来源(db +文件系统)获取信息,一个repo将充当外观,但它仍然是DAO的另一个名称.

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