使用RavenDB实现存储库和服务模式

我在RavenDB项目中实现存储库和服务模式有些困难。 主要关注的是我的存储库界面应该是什么样子,因为在RavenDB中我使用了几个索引来查询。

假设我需要获取parentid等于1的所有项目。一种方法是使用IQueryable List()并获取所有文档,然后添加一个where子句来选择parentid等于1的项目。这似乎是一个坏主意因为我无法在RavenDB中使用任何索引function。 所以另一种方法是在存储库中有类似这样的东西,IEnumerable Find(字符串索引,Func谓词),但这似乎也是一个坏主意,因为它不够通用,并且要求我实现这个方法,如果我从RavenDB更改到一个常见的SQL服务器。

那么如何实现通用存储库,但仍然可以在RavenDB中获得索引的好处?

这篇文章很好地总结了一切:

http://novuscraft.com/blog/ravendb-and-the-repository-pattern

首先,问一下为什么要使用存储库模式?

如果您想要使用该模式,因为您正在进行域驱动设计,那么当另外一个答案指出时,您需要重新考虑查询的意图,并根据您的域名进行讨论 – 以及你可以开始围绕这个模拟事物。

在这种情况下,规格可能是你的朋友,你应该调查它们。


但是,在继续我的回答之前,让我们暂时看一下你问题的一部分:

看起来是一个坏主意,因为它不够通用,并且要求我实现此方法,如果我将从RavenDB更改为常见的SQL服务器。

你会以错误的方式解决这个问题 – 尝试让你的系统完全与持久性无关在这个级别上遇到麻烦 – 如果你尝试从查询本身隐藏数据存储区的独特function那么为什么要使用RavenDB呢?

我倾向于在简单的面向文档中使用的方法(IE,我就数据而言,这是你似乎正在做的事情),是从我的命令中分离我的查询。

问问自己,为什么要按父ID查询文档? 它是否在页面上显示列表? 你为什么试图用文件来模拟这个呢? 为什么不根据视图模型对此进行建模,并使用从RavenDB中检索此数据的最有效方法? (对索引进行查询(动态或其他)),将其粘贴在工厂中,该工厂采用“一些输入”并生成“输出”,如果您决定更改持久性存储,则可以更改这些工厂。 (我在我的ASP.NET MVC应用程序中更进了一步,并且有单个动作控制器,我不称它们为控制器,在大多数情况下从这些控制器进行查询)。

如果你想通过父ID实际提取你的文件,以便更新它们或在它们之间运行一些业务逻辑,也许你已经将它们建模错误了 – 写操作通常只涉及改变单个文档,或者换句话说您应该围绕事务边界对文档进行建模。

TL; DR

考虑一下您实际想要实现的目标 – 为什么要使用“存储库模式”或“服务模式” – 如果您围绕您的应用程序建模应用程序,这些单词作为描述您最终可能会遇到的方案的方式存在需要,作为表达某个对象角色的常用方式 – 而不是你需要将你的每一个function都塞进去的东西。

假设我需要获取parentid等于1的所有项目。

首先,不要以这种方式考虑您的数据访问需求。

你不需要“ 获取parentid等于1的所有项目 ”。 它将有助于尝试以这种面向数据的方式思考。

您需要的是获取具有特定父级的所有项目。 这是一个存在于您的问题空间(您的应用程序的域)中的概念。

使用外键和名为parentid的字段在数据库中对此进行建模的事实是一个实现细节 。 封装它,不要在整个应用程序中泄漏它。

一种方法是使用IQueryable List()并获取所有文档,然后添加一个where子句来选择parentid等于1的项。这似乎是一个坏主意,因为我不能在RavenDB中使用任何索引function。 所以另一种方法是在存储库中有类似这样的东西,IEnumerable Find(字符串索引,Func谓词),但这似乎也是一个坏主意,因为

这两个都是坏主意。 您的建议是要求调用存储库或查询的代码了解您的架构。

为什么您的存储库的消费者应该关心或知道存在parentid字段? 如果这种情况发生变化,如果问题空间中某个特定概念的定义发生变化,那么代码中需要更改多少个位置?

每个用特定父级获取项目的地方。

这很糟糕,它是封装的对立面。

我的观点是,您将希望将查询建模为显式概念,而不是传递的lambda或字符串并全部使用。

您可以使用规范模式,存储库上的命名查询方法,查询对象模式等显式建模查询。

它不够通用,并且要求我实现此方法,如果我将从RavenDB更改为常见的SQL Server。

好吧,那个Func 通用了。 再次考虑一下,为了使用这种查询方法,您的消费代码需要知道什么,您将直接将代码的上层绑定到您的数据库模式。

此外,如果您从一个存储引擎更改为另一个存储引擎,则无法避免重新实现查询,其中性能足以成为使用特定于存储引擎的辅助工具(例如,Raven中的索引)的因素。

我实际上不鼓励你使用存储库模式。 在大多数情况下,它过度架构并且实际上使代码更复杂。

Ayende最近为此发了一些post:

我建议只针对Raven的原生API。

如果您觉得我的回答过于笼统,请列出您希望通过使用另一层抽象获得的一些好处,我们可以继续讨论。