【问题标题】:Abstracting away database specific id:s with the repository pattern?使用存储库模式抽象出特定于数据库的 id:s?
【发布时间】:2011-08-11 16:14:53
【问题描述】:

我正在学习 DDD(领域驱动设计)和存储库模式(在 C# 中)。我希望能够使用存储库模式来持久化实体,而不关心实际使用的是哪个数据库(Oracle、MySQL、MongoDB、RavenDB 等)。但是,我不确定如何处理大多数(全部?)数据库使用的特定于数据库的 id。例如,RavenDB 要求它应该存储的每个实体都有一个字符串类型的 id 属性。其他可能需要 int 类型的 id 属性。由于不同的数据库对此的处理方式不同,因此我无法将数据库 id 作为实体类的一部分。但它必须在某个时候存在,至少在我存储实际实体时。我的问题是这方面的最佳做法是什么?

我目前追求的想法是,对于我想要支持的每个数据库,为每个业务对象类型实现数据库特定的“值对象”。然后,这些值对象将具有数据库特定的 id 属性,并且我将在读取和写入时在两者之间进行映射。这看起来是个好主意吗?

【问题讨论】:

  • 你真的需要它吗?对我来说似乎不是一个好主意,因为你会遇到维护地狱。
  • 我也面临同样的问题。不幸的是,这里提到的解决方案都没有把我称为“完美的解决方案”(如果有的话......)。我还考虑过在数据抽象层中转换为特定的实现类型。我的意思是,虽然 DTOs 和 DAOs 是所有 DB 类型共有的接口,但任何特定的 DAO 实现都会将其方法中的 DTOs 转换为相同的特定实现类型(我是假设一次只使用一种 DB 类型)。这样,它将能够使用特定的ID。它看起来很脏,但这是我迄今为止想出的最好的。
  • 我刚刚发现了另一个关于类似问题的问题,有一个答案似乎也可以在这里工作。看看:stackoverflow.com/questions/10785598/…

标签: c# database domain-driven-design repository


【解决方案1】:

这是泄漏抽象的经典案例。除非您想放弃每个数据库附带的所有优点,否则您不可能抽象出存储库接口下的数据库类型。对 ID 类型(字符串、Guid 或其他)的要求只是巨大冰山的最顶端,其大部分质量在浑水之下。

想想事务处理、并发和其他东西。我理解你关于持久性无知的观点。确保不依赖域模型中的特定数据库技术是一件好事。但是你也无法摆脱对任何持久性技术的依赖。

让您的域模型与任何 RDBMS 一起工作都相对容易。它们中的大多数具有标准化的数据类型。使用像 NHibernate 这样的 ORM 会对你有很大帮助。在 NoSQL 数据库中实现同样的目标要困难得多,因为它们往往差异很大(实际上这非常好)。

因此,我的建议是对您必须处理的可能的持久性技术集进行一些研究,然后为持久性子系统选择适当的抽象级别。

如果这对您不起作用,请考虑事件溯源。事件存储是要求最低的持久性技术之一。使用 Jonathan Oliver 的 EventStore 等库,您可以使用几乎任何存储技术,包括文件系统。

【讨论】:

  • 不,这是相反的——一个避免泄漏抽象的经典案例。使用商店的 ID 类型作为对象的全局“友好”引用是有漏洞的。让商店坚持这个 ID 很好并且通常是必要的。通常,这两件事很愉快地重合。这小子想把两者分开,所以他必须解决这个问题。
  • @Chalky 我同意你的观点,当涉及到主要的 API 提供者(如数据库)时,(几乎总是)没有太多抽象。如果您非常依赖 DB 类型,以至于在代码中的任何地方都使用特定的 ID 类型,那么您将无法更改 DB ......好吧,几乎永远!一旦数据库充满了数据,公司就不愿意用另一个数据库替换他们正在使用的数据库,这也很大程度上是因为在大多数情况下,这也意味着提供一个全新的软件实现,由于大规模耦合造成的类型假设一个特定的 ID 类型。
【解决方案2】:

我会继续在实体中创建一个 int Id 字段,然后将其转换为存储库中的字符串,其中 Id 必须是字符串。我认为抽象您的持久性的努力是非常值得的,并且实际上简化了维护。

【讨论】:

    【解决方案3】:
    1. 您正在做正确的事情!将自己从数据库主键类型的约束中抽象出来!

    2. 不要尝试翻译类型,只需使用不同的字段即可。

    特别是:不要尝试使用数据库的主键,除非在您的数据访问逻辑中。如果您需要一个对象的友好 ID,只需创建一个您喜欢的任何类型的附加字段,并要求您的数据库存储它。只有在您的数据访问层中,您才需要根据对象的友好 ID 查找和更新数据库记录。很简单。

    然后,您对哪些数据库可以保存您的对象的限制已从“必须能够具有 xxxx 类型的主键”更改为简单的“必须能够存储 xxxx 类型”。我想你会发现你可以使用世界上的任何数据库。快乐编码! DDD 是最好的!

    【讨论】:

    • 我在问题中也遇到了同样的问题。你的建议是我想到的第一件事。唯一让我烦恼的是,它可能会在数据库中的几乎任何对象中创建两个不同的 ID 类型字段。
    【解决方案4】:

    您可以在实体中拥有 ID,但不能将其作为实体公共接口的一部分公开。 NHibernate 可以做到这一点,因为它允许您将表列映射到私有字段。

    所以你可能有类似的东西

    class Customer {
            private readonly Int32? _relationalId;
            private readonly String? _documentId;
            ...
    

    这并不理想,因为您的持久性逻辑在业务逻辑上“流血”,但考虑到要求,它可能比在实体外部某个位置维护实体与其 id 之间的映射更容易、更健壮。我还强烈建议您评估“与数据库无关”的方法,如果您只想支持关系数据库,这将更加现实。在这种情况下,您至少可以将像 NHibernate 这样的 ORM 重用于您的存储库实现。并且大多数关系数据库都支持相同的 id 类型。在您的场景中,您不仅需要 ORM,还需要“Object-Document-Mapper”之类的东西。我可以看到您将不得不编写大量的基础设施代码。我强烈建议您重新评估您的要求并在关系数据库和文档数据库之间进行选择。阅读:Pros/cons of document-based databases vs. relational databases

    【讨论】:

      猜你喜欢
      • 2017-09-20
      • 2013-07-31
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 2013-05-01
      • 2014-10-26
      相关资源
      最近更新 更多