【问题标题】:DDD - How to store aggregates in NoSql databasesDDD - 如何在 NoSql 数据库中存储聚合
【发布时间】:2017-11-01 10:36:05
【问题描述】:

当前项目需要我们将域对象持久化到 NoSQL 数据库(例如 mongoDB)中。 在许多示例中(包括 Eric Evans、Vaughn Vernon),域对象被序列化并直接持久化到 mongoDB。

我们希望通过在我们的域对象中不包含任何注释来避免将域层与持久性相关的信息混为一谈。 我们还担心将来会通过更改域对象来破坏持久化的数据。

我们得出的结论是,我们需要某种 DTO 在域对象和持久数据之间进行转换。

你们中的任何人有没有为这种情况找到一个好的解决方案?

【问题讨论】:

  • 我正在使用反射从 mongo 映射到 PHP 对象
  • 为什么不使用带有 xml 映射的 ORM?
  • @ConstantinGALBENU 为什么在非关系数据的上下文中提到 ORM,即对象关系映射?我很迷惑。对象被序列化为 BSON 并按原样存储在任何文档数据库中,包括 MongoDb。

标签: mongodb domain-driven-design ddd-repositories


【解决方案1】:

您可以将域对象按原样存储在文档数据库中。 Vaughn Vernon 发表了一篇关于此的文章 The Ideal Domain-Driven Design Aggregate Store?,其中介绍了 PostgreSQL 新的(当时)JSONB 类文档存储。

当然,您可能会面临聚合被BsonX 属性污染的风险,这可能是您不希望的。您可以通过使用约定配置来避免这种情况,但您仍然需要考虑序列化,这可能会影响封装级别。

这里的另一种模式是使用单独的状态对象,然后将其作为属性保存在聚合根(或常规实体)内。我不会称它为“DTO”,因为这显然是您的聚合状态。你没有转移任何东西。聚合中的方法可以改变状态,或者更好的是,状态将是一个不可变的值对象,当你需要改变状态时会产生新的状态。

在这种情况下,持久性只关心状态对象。您可能仍然不喜欢在状态对象属性上使用 MongoDb 属性,这是合理的。然后,您需要在持久性机制中具有相同的结构,以便将属性映射到一个。

【讨论】:

    【解决方案2】:

    和您一样,我希望业务对象不依赖于任何类型的特定存储库。我是这样解决的:让您的业务对象将其状态对象和存储库函数定义为接口。您的存储库实现可以创建一个实际的状态对象,并使用构造函数将其注入到您的业务对象中。

    这种方法有很多优点(例如具有用于特定目的的业务对象),但是您可以通过这种方式轻松实现存储库的完全(双向)独立性。 Martin Fowler 在其他地方也暗示了这种方法。

    我实际上在我的 Angular / TypeScript 项目中使用了相同的模式。我的 read-api 调用返回 DTO 对象,这些对象也注入了状态对象,并且它们的属性直接映射到状态对象上。

    这些 DTO 在从 api 到客户端 (Angular) 项目时最终成为无类型的 javascript 对象,然后依次作为状态对象注入 TypeScript 对象,再次注入构造函数并由 getter 和 setter 映射。它工作得非常干净并且易于维护。我的 GitHub (niwra) 帐户(软件管理存储库)上有一个示例,但如果有人感兴趣,可以在这里展开。

    MongoDB 允许非常干净且可单元测试的存储库实现,它返回强类型聚合。我唯一还没有完全解决的问题是告诉 MongoDb 子集合的状态对象。目前这仍然是相当“静态”的,但我相信我会找到一些不错的解决方案。

    【讨论】:

      【解决方案3】:

      当前项目需要我们在 NoSQL 中持久化领域对象 数据库,例如 mongoDB。在许多示例中(包括 Eric Evans、Vaughn Vernon) 域对象被序列化并持久化到 mongoDB 直接。

      我可以确认MongoDB 是持久化DDD 模型的好选择。我在当前项目中使用MongoDB 作为Event store。即使不使用Event sourcing,也可以使用MongoDB,例如使用ODM(对象文档映射器):每个Aggregate 实例都有一个文档(这适用于任何基于文档的数据库,不仅适用于@987654330 @) 并将嵌套的 entitiesvalue objects 存储为嵌套文档。

      我们希望通过在我们的领域对象中不包含任何注释来避免将领域层与持久性相关的信息混为一谈。

      你可以使用xml映射。

      我们还担心将来会通过更改域对象来破坏持久化数据。

      为此,您可以使用自定义迁移脚本。如果你使用Event sourcing,那么就有event versioning strategies

      我们得出的结论是,我们需要某种 DTO 在域对象和持久数据之间进行转换。

      这是bad conclusion

      如果您使用 CQRS,则不需要 DTO,因为 readmodels 就足够了。

      【讨论】:

        【解决方案4】:

        是的。你的领域模型应该不知道持久性。所以你需要一个 DTO 或者我所说的数据模型(除了领域模型和视图模型)。在持久化到数据库之前,您的数据模型将映射到域模型。这种映射在插入和更新操作中很常见。对于只读操作(报告等),您可以绕过从数据模型到域模型的映射。这将阻止加载域模型的整个对象图。这在读写命令分离的 CQRS 架构模式中得到广泛应用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-06
          • 2012-08-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-10-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多