将存储库模式与文档数据库一起使用是否有意义?

时间:2011-09-21 10:01:33

标签: design-patterns nosql data-access-layer document-database

我目前正在尝试 MongoDB 。我正在从 NHibernate / SQL 思维模式转向,所以最初我实现了存储库模式以进行数据访问。

在我开始使用嵌套文档之前,这一切都很好看。现在看起来似乎有点不匹配。但是,我对存储库感到满意,比如抽象,关注点分离和可测试性。

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

6 个答案:

答案 0 :(得分:14)

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

我猜你必须考虑到存储库是什么以及从额外的抽象层获得的优势。一个好的设计将具有最少的层数可能

因此,存储库为您提供了一些持久性无知以及在公共数据上下文中使用工作单元的能力。它还可以提高您单独测试数据查询的能力(因为它们通常被抽象为可查询或规范)。

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

因此,在我看来,使用存储库并不是关于您的数据是存储为关系表还是文档,而是关于您从抽象中获得的更多信息。

答案 1 :(得分:2)

我不知道这对你是否会有所帮助,但是我和Ayende Rahien在谈论RavenDB的时候正在听pod-cast。他建议文档实际上很好地映射到DDD哲学中的聚合。如果您使用嵌套文档来表示聚合并遇到设计问题,那么嵌套可能不是最好的方法吗?

答案 2 :(得分:2)

考虑“我是否应该使用X”并不像专注于“我应该使用什么”那样富有成效?

存储库模式有哪些替代方案,有哪些权衡,以及它们与您的域和实现有何关联?

存储库适用于在通用存储(例如SQL)上实施预定义的模式集。对于文档存储,我的印象是文档架构将比通常在基于SQL的存储中看到的更大程度地确定访问模式。在这种情况下实现存储库可能会导致非常漏洞的抽象,其中对基础文档结构的更改会对相关业务代码产生1:1的影响。在这种情况下,存储库提供的价值非常小。对我来说,文档存储自然很适合工作单元(UoW)范例,其中工作单元是文档(或doc +嵌套的子文件或文档集)。

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

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

如果您可以提供有关您的域名和实施的更多详细信息,那么我们可以深入了解您可能需要考虑的具体权衡

答案 3 :(得分:2)

我在使用存储库模式的生产代码中使用MongoDB已超过2年了,我可以说随着时间的推移它确实帮助了我。抽象适用于测试(内存中)和生产(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()命令实现提交,根据我的经验,该命令运行得不好。这对我来说不是问题,因为我几乎从未明确提交过更改。

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

在NoSQl数据库中使用存储库模式比在RDS数据库中使用更有意义,因为在适当的DDD中,每个聚合根需要一个存储库,但是由于RDS数据存储的局限性,开发人员通常为每个实体/表创建一个存储库对象。 NoSQL数据库允许您正确实现DDD和存储库模式。