当前项目需要我们在NoSQL数据库(如mongoDB)中持久保存域对象。 在许多示例中(包括Eric Evans,Vaughn Vernon),域对象被序列化并直接保存到mongoDB。
我们希望通过在域对象中不添加任何注释来避免将域层与持久性相关的信息混合。 此外,我们还担心将来通过更改域对象来破坏持久数据。
我们得出的结论是,我们需要在域对象和持久数据之间进行某种DTO转换。
你们有没有人遇到过这种情况的好解决方案?
答案 0 :(得分:2)
与您一样,我希望业务对象不依赖于任何类型的特定存储库。我这样解决了:你的业务对象将其状态对象和存储库函数定义为接口。您的存储库实现可以创建一个实际的状态对象,并使用构造函数将其注入您的业务对象。
此方法有很多优点(例如,为特定目的提供业务对象),但您可以通过这种方式轻松实现存储库的完全(双向)独立性。 Martin Fowler也在其他地方暗示了这种方法。
我实际上在Angular / TypeScript项目中使用了相同的模式。我的read-api调用返回DTO对象,它们也会注入状态对象,并且它们的属性直接映射到状态对象上。
这些DTO最终作为无类型的javascript对象,当它们从api到客户端(Angular)项目时,然后作为状态对象注入到TypeScript对象中,再次在构造函数中注入并由getter和setter映射。它工作非常干净,维护良好。我在我的GitHub(niwra)帐户(软件管理存储库)上有一个示例,但如果有人感兴趣,可以在这里扩展。
MongoDB允许非常干净且可单元测试的存储库实现,它返回强类型聚合。我还没有彻底解决的唯一问题是告诉MongoDb关于子集合的状态对象。目前这仍然非常“静态”,但我相信我会找到一些不错的解决方案。
答案 1 :(得分:1)
是。您的域模型应该不了解持久性。因此,您需要一个DTO或我称之为数据模型(除了域模型和视图模型)。在保留到数据库之前,您的数据模型将映射到域模型。此映射在插入和更新操作中非常常见。对于只读操作(报告等),您可以绕过从数据模型到域模型的映射。这将阻止加载域模型的整个对象图。这在CQRS架构模式中得到广泛应用,其中读写命令是分开的。
答案 2 :(得分:1)
当前项目需要我们在NoSQL中持久保存域对象 数据库如mongoDB。在许多例子中(包括Eric Evans,Vaughn Vernon)域对象被序列化并持久化到mongoDB 直接
我可以确认MongoDB
是保留DDD
模型的不错选择。我在当前项目中使用MongoDB
作为Event store
。即使您没有使用MongoDB
,也可以使用Event sourcing
,例如使用ODM(对象文档映射器):每个Aggregate
实例都有一个文档(这适用于任何基于文档的文档)数据库,不仅MongoDB
),而且您将嵌套的entities
和value objects
存储为嵌套文档。
我们希望通过在域对象中不添加任何注释来避免将域层与持久性相关的信息混合。
您可以使用xml映射。
此外,我们还担心将来通过更改域对象来破坏持久数据。
为此,您可以使用自定义迁移脚本。如果您使用Event sourcing
,则会有event versioning strategies。
我们得出的结论是,我们需要在域对象和持久数据之间进行某种DTO转换。
如果您使用CQRS
,则不需要DTO,因为readmodels
已足够。
答案 3 :(得分:0)
您可以按原样将域对象存储在文档数据库中。 Vaughn Vernon发表了一篇关于此问题的文章The Ideal Domain-Driven Design Aggregate Store?,其中包括PostgreSQL new(当时)JSONB文档存储。
当然,您可能会面临被BsonX
属性污染的聚合的风险,这可能是您不想要的。您可以通过使用约定配置来避免这种情况,但您仍然需要考虑序列化,这可能会对封装级别产生影响。
此处的另一种模式是使用单独的状态对象,然后将其作为聚合根(或常规实体)内的属性保存。我不会称之为" DTO",因为这显然是你的聚合状态。你不是转移任何东西。聚合中的方法可以改变状态,或者更好的是,状态将是一个不可变的值对象,当你需要改变状态时会产生新的状态。
在这种情况下,持久性只关心状态对象。您可能仍然不悦地对状态对象属性MongoDB的属性和这是合理的。然后,您需要在持久性机制中具有相同的结构,因此您可以将属性映射到一个。