【问题标题】:Database: Repositories with NoSQL/Document Database (DDD)数据库:带有 NoSQL/文档数据库 (DDD) 的存储库
【发布时间】:2019-01-06 01:21:48
【问题描述】:

希望从已将其存储库从关系数据库迁移到 NoSQL 的任何人那里获得任何建议?

我们目前正在使用 Postgres 数据库和 ORM (SQLAlchemy) 构建应用程序。但是,以后我们可能需要将应用程序迁移到当前仅支持几个 NoSQL 解决方案的环境。

考虑到这一点,我们对 Vaughn Vernon 的实施领域驱动设计中涵盖的存储库采用了面向持久性的方法。这会产生以下 API:

save(aggregate)
save_all(aggregates)
remove(aggregate)
get_by_...

无需赘述,ORM 特定代码已隐藏在存储库本身中。 Session 仅用于检索或更新数据,然后立即提交和关闭(在 repos 方法中)的短时间内。这意味着在保存时进行大量合并,而不是最有效地使用 Session。

def save(aggregate):
try:
session.merge(aggregate)
commit
except:
rollback

def get():
try:
aggregate = session.query_by(id)
session.expunge
commit
except:
rollback
return aggregate

等等等等

优点:

  • 我们将自己限制为每个用例更新单个聚合,因此在应用程序服务中充分利用 UOW 事务控制的可能性很小(除了性能之外)。在写入聚合时,会在存储库中启用事务控制,以确保保留完整的聚合。
  • 在存储库之外没有 ORM 特定的代码泄漏,无论如何在切换到 NoSQL 数据库时都需要重新编码。

因此,如果我们确实必须切换到 NoSQL 数据库,我们应该做的工作量最少。

然而,我读过的几乎所有内容都鼓励事务行为存在于应用程序服务层中。尽管我认为 Business Transactional 和 DB Transactional 之间存在区别。

同样,我们正在对性能造成影响,因为我们在每次调用存储库时都要求会话工厂进行会话。大多数服务包含大约 3 次对存储库的调用。

那么,对于从关系数据库迁移到 NoSQL 数据库的任何人来说,问题是什么?

  • 工作单元/会话的概念在 NoSQL 世界中是否有意义?

  • 我们是否应该同时完全接受 ORM,并将 UOW/Session 从存储库移到应用程序服务中?

  • 如果我们这样做,如果我们最终需要迁移到 NoSQL 解决方案,那么重新设计应用程序服务的工作量是多少。 (在任何情况下都需要重新编写存储库)。

  • 最后,有人在编写与实现无关的存储库方面有丰富的经验吗?

附言。了解我们可以完全放弃 ORM,同时使用纯 SQL,但我们被要求确保我们使用的是 ORM。

【问题讨论】:

  • 是的,但当前数据库是关系数据库。问题涉及以一种方式对当前应用程序进行编码,以使将来尽可能轻松地切换到非关系数据库(因为已经向我们表明,我们可能需要这样做,这超出了我们的控制范围。)。

标签: database orm nosql domain-driven-design repository-pattern


【解决方案1】:

编辑:在这个答案中,我根据问题标题关注文档数据库。当然,其他 NoSQL 存储具有截然不同的特征(例如图形数据库、使用事件溯源等)。


这应该不是问题。

在文档数据库中,您的整个聚合应该是一个文档。这样,您就获得了事务一致性所需的完全相同的保证。无论聚合中有多少实体发生变化,您仍在存储文档。您需要确保执行某种形式的乐观并发(通过 etag 或版本或类似方式),而不是工作单元模式,但在此之后,您的事务需求就会得到满足。

我无法真正评论您现在是否完全采用 UoW 模式,而不是依赖 ORM 实现等。这在很大程度上取决于您当前的情况和有关实现的细节。不过我可以说的是,您很可能不需要一次性从标准格式 (SQL) 迁移到文档。从一个简单的开始,这样您就可以了解哪些对您有用,哪些对您无效。

我不知道是否存在与实现无关的存储库,但这对我来说没有多大意义。存储库的全部意义在于封装持久性,因此您无法对其进行抽象:不会分配给它们任何其他职责。此外,您不能假设存储库需要将不同的模型组合到聚合模型中:这是特定于平台的,因此它不是不可知的。

最后一条评论:我在你的问题中看到你写的文件save_all(aggregates)。我不确定您指的是什么,但至少,每个聚合保存都应该包装在自己的事务中,否则此操作违反聚合的事务边界特性。

【讨论】:

  • 谢谢你。大反响。我们已经在使用乐观并发和一个版本,很高兴听到这个消息。
  • 就实现无关的存储库而言。我本来可以更清楚的。这更像是 ORM 通过 Session/UOW 泄漏到应用程序层的情况。听起来,通过在断开状态下处理数据并将 Session/UOW 隐藏在 repo 中,我们正在模仿我们将使用 NoSQL DB 和乐观并发进行的操作。 save_all 方法遵循弗农的书。向存储库添加聚合根(相同类型)的集合非常方便。
  • @SKleanthous 并非所有 NoSQL 数据库都是文档数据库,尽管它们与 DDD 聚合配合得很好。
  • @guillaume31 你当然是对的,我知道,但提出的问题是在标题中询问文档 db。因此,我的答案只集中在这个上面。例如,就我个人而言,我偏爱事件溯源来为我的域建模。其他解决方案仍然可能有其他好处(例如具有流式传输功能的内存数据库)。
【解决方案2】:

工作单元/会话的概念在 NoSQL 中是否意味着什么 世界?

是的,它仍然是一个有趣的概念。仅仅因为您使用的是 NoSQL 存储并不意味着对某种业务事务管理的需求就消失了。许多 NoSQL 数据库都有驱动程序或第三方库来管理更改跟踪。例如,请参阅RavenDB

当然,如果您只为每个事务加载一个聚合,并且如果您的 NoSQL 存储单元与聚合完美匹配,那么工作单元的大部分功能将不那么重要,但您仍然会面临例外情况规则。此外,在任何情况下都相关的 UoW 部分是 Commit,也可能是 Abort。

与此同时,我们是否应该完全接受 ORM,并移动 存储库之外的 UOW/Session 进入应用程序服务?

我建议在一个成熟的类中具体化工作单元的概念:

class UnitOfWork {
  void Commit() 
  {
    // Call your ORM persistence here
  }
}

应用程序服务只是调用工作单元的地方,而不是实现它的地方。

如果我们这样做,重新设计的努力程度如何? 应用服务,如果我们需要迁移到 NoSQL 解决方案中 结尾。 (在任何情况下都需要重新编写存储库)。

这取决于许多其他参数,例如 NoSQL API 或第三方库对工作单元的支持,以及聚合和 NoSQL 存储之间的形状相似性。它的范围可以从几乎没有工作到自己编写完整的 UoW/更改跟踪实现。如果是后者,将 UoW 逻辑从 Repository 提取到单独的类将不是工作中最难的部分。

最后,任何人都有很多编写与实现无关的经验 仓库?

我在这里同意 SKleanthous - 与实施无关的存储库在 IMO 中没有多大意义。您的存储库抽象(接口)当然是不可知的,但在实现方面,您必须解决特定的持久存储问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2012-10-09
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 1970-01-01
    相关资源
    最近更新 更多