将存储库模式用于文档数据库是否有意义?

问题描述 投票:25回答:6

我目前正在尝试MongoDB。我从NHibernate / SQL心态转变,因此最初我实现了存储库模式进行数据访问。

在我开始使用嵌套文档之前,一切都还不错。现在开始似乎有点不匹配。但是,我对存储库感到满意,并且喜欢它们提供的抽象,关注点分离和可测试性。

人们是否成功将存储库模式用于文档数据库?如果没有,您将使用哪种数据访问方法?那么抽象/ SoC呢?

design-patterns nosql data-access-layer document-database
6个回答
14
投票

这是一个有趣的问题。在使用MongoDB时,我选择了没有存储库。这是主要的,因为文档数据库被用作读存储(因此简化了存储在其中的数据)。

我想您必须将考虑范围退回到什么是存储库以及从额外的抽象层中获得什么好处。一个好的设计将具有最少数量的层[[possible。

因此,存储库使您对持久性有所了解,并能够在通用数据上下文中使用工作单元。它还可以提高您隔离测试数据查询的能力(因为这些通常通常抽象为查询或规范)。

而且,某些文档数据库已经提供了存储库模式(RavenDB等),因此不需要再有另一层。

所以,在我看来,使用存储库并不是关于数据是存储在关系表还是文档中,而是从抽象中获得的收益更多。


2
投票
我不知道这是否对您有帮助,但是前一段时间我在听pod-cast时,Ayende Rahien在谈论RavenDB。他建议文档实际上可以很好地映射DDD哲学中的汇总。如果您使用嵌套的文档来表示一个集合并遇到设计问题,也许嵌套不是最好的方法?

2
投票
关于“我是否应该使用X”的思考不如关注“我应该使用什么”有效?

存储库模式的替代方案有哪些,权衡因素是什么,它们与您的域和实现有何关系?

存储库非常适合在通用存储(例如SQL)上强制执行一组预定义的模式。对于文档存储,我的印象是,与基于SQL的存储相比,文档架构将更广泛地确定访问模式。在这种情况下,实施存储库可能会导致非常抽象的泄漏,其中对基础文档结构的更改对相关的业务代码有1:1的影响。在这种情况下,存储库提供的价值很小。对我而言,文档存储自然很适合工作单元(UoW)范式,其中工作单元是文档(或doc + nested子文档,或文档集)。

正如您提到的,存储库模式的另一个优势是对存储机制的抽象。折衷方案通常是无法访问Mongo的低层特定于实现的功能。这对您来说是值得的权衡吗? NHibernate与SQL紧密耦合,因此对RDBMS的所有重要功能都具有丰富的功能抽象。我不知道Mongo有任何类似的框架,因此您确实会大大提高抽象水平。

您是否需要支持多个并发数据存储?例如,您是否将通过相同的数据层抽象将某些类型的数据写入SQL,将其他类型的数据写入Mongo?如果是这样,那么存储库是一个不错的选择。

如果您可以提供有关您的域和实现的更多详细信息,那么我们可以进一步深入研究您可能要考虑的特定折衷方案


2
投票
我使用Regoory模式在生产代码中使用MongoDB已有两年多了,我可以说,随着时间的推移,它确实对我有所帮助。这些抽象非常适合测试(内存中)和生产(MongoDB)。

我使用Java,代码看起来像这样(大致):

public interface MyStorage { boolean add(MyDoc doc); boolean update(MyDoc doc); boolean remove(String docId); boolean commit(); MyDoc get(String docId); MyStorageQuery newQuery(); List<MyDoc> query(MyStorageQuery q); }

我为每个存储实现都有一个工厂,该工厂创建MyDoc对象的新实例。我在MongoDb和自己的手动模拟之间交换了实现以进行测试。

MongoDB实现使用MyDoc类来扩展BasicDBObject,如下所示:

public interface MyDoc { Data getData(); // let's assume this is a nested doc void setData(Data d); String getId(); long getTimestamp(); void setTimestamp(long time); } MongoDbMyDoc extends BasicDBObject implements MyDoc { MongoDbObject() { } void setId(String id) { this.put("_id", id); } String getId() { return super.get("_id"); } void setData(Data d) { dataObj = new BasicDBObject(); dataObj.put("someField",d.someField); dataObj.put("someField2", d.someField2); super.put("data",dataObj); } ... }

然后,我在实际的存储实现中使用MongoDB Java客户端从数据库返回我的实现的实例。这是我的MongoDB存储实现的构造函数:

public MongoDbMyStorage(DB db, String collection) { //DB in a mongodb object (from the client library) which was instantiated elsewhere dbCollection = db.getCollection(collection); dbCollection.setObjectClass(MongoDbMyDoc.class); this.factory = new MongoDbMyDocFactory(); }

这里还有2个接口:MyStorageQuery也被实现为MongoDB实现的BasicDBObject,并使用存储接口的newQuery()生成。MyDocFactory(此处未显示),但基本上是一个文档工厂,它知道什么是存储实现,并相应地生成MyDoc实例。

注意事项:抽象没有多大意义的一件事是定义MongoDB存储使用的索引。我把所有的ensureIndex(...)调用都放在了构造函数中,不是很通用,但是为每个集合定义索引是MongoDB特定的优化,因此我可以使用它。另一个是使用getLastError()命令来实现提交,根据我的经验,该命令效果不佳。对我来说这不是问题,因为我几乎从不明确地进行更改。


0
投票
Eric Evens,在他的“域驱动设计”书中,对存储库模式有非常复杂且非常好的解释。他的定义是存储库应该是什么以及应该如何使用它(以我个人的观点)。您可以在此处找到简短说明:Eric Evans on Repositories

[基本上,如果您将存储库保留为客户端代码和工厂之间的中介,那么它们将非常适合我所需要的内容。存储库应提供查询/构造/验证接口,并负责所有数据采集人员(如连接/查询数据库),并且您应具有一个或多个(根据需要)工厂,这些工厂将构建对象并将其传递回客户端通过存储库。


0
投票
在NoSQl数据库中使用存储库模式比在RDS数据库中使用更有意义,因为在正确的DDD中,每个聚合根都需要一个存储库,但是由于RDS数据存储的局限性,开发人员通常为每个实体/表创建一个存储库对象。 NoSQL数据库允许您正确实现DDD和存储库模式。
© www.soinside.com 2019 - 2024. All rights reserved.