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

Mar*_*cus 11 c# oop design-patterns ravendb

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

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

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

Rob*_*ton 10

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

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

在这种情况下,规格可能是你的朋友,你应该研究它们.


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

看起来是个坏主意,因为它不够通用,并且要求我实现这个方法,如果我从RavenDB更改为常见的sql server.

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

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

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

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

TL; DR

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


que*_*rin 8

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

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

不要需要" 取其中的parentid等于1的所有项目 ".它将有助于尝试以这种面向数据的方式思考.

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

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

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

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

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

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

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

我的观点是你会想要将查询建模为显式概念,而不是传递的lambda或字符串并全部使用.

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

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

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

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


Chr*_*age 5

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

Ayende最近为此发表了许多帖子:

我建议只针对Raven的本机API进行编写。

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

  • 也许有些过时,但是我发现许多Ayende博客都提供了糟糕的建议。如果没有那么多开发人员支持他,他建议放弃数据抽象层而直接从客户端代码调用ORM方法将是可笑的。我想知道他对YAGNI的基本原理如何对待所有想要切换到RavenDB但不能这样做的开发人员,因为他们的应用程序现在已经与NHiberbate紧密结合... (6认同)
  • 没有答案,那就是答案。 (2认同)