Node.js 如何实现noSQL数据库抽象?

Node.js 如何实现noSQL数据库抽象?,node.js,model,couchdb,domain-driven-design,clean-architecture,Node.js,Model,Couchdb,Domain Driven Design,Clean Architecture,我使用CouchDB,一个面向文档的数据库,它将数据存储为JSON文档。我在后端使用Javascript,因此可以直接存储JS对象。目前,我在CouchDB中存储的模型和我的域层之间有一个直接映射。我认为这不是一件好事,因为我完全依赖于这个数据库 例子 我有两个实体用户和项目。 一个用户可以有多个项目,这些模型在数据库中表示为单独的文档: 用户文档 { "_id": "1", "entity": "user" } 项目文档 该关系由属性用户id引用 { "_id": "2",

我使用CouchDB,一个面向文档的数据库,它将数据存储为JSON文档。我在后端使用Javascript,因此可以直接存储JS对象。目前,我在CouchDB中存储的模型和我的域层之间有一个直接映射。我认为这不是一件好事,因为我完全依赖于这个数据库

例子 我有两个实体用户项目。 一个用户可以有多个项目,这些模型在数据库中表示为单独的文档:

用户文档

{
  "_id": "1",
  "entity": "user"
}
项目文档
该关系由属性用户id引用

{
  "_id": "2",
  "entity": "project",
  "user_id": "1",
  "name": "project1"
}

// other project document
{
  "_id": "3",
  "entity": "project",
  "user_id": "1",
  "name": "project2"
}
我的问题是:
我应该在层之间保持这种方便的映射,还是应该创建一个抽象层,将模型从数据库转换为域对象?

更方便的用户域对象示例:

// _id property is replaced by id
// entity property is removed
// projects relations stand in a projects array
{
  id: "1",
  projects: [
   { id: "2", name: "project1"},
   { id: "2", name: "project1"}
  ]
}

注:我想阅读Eric Evans的《领域驱动设计》一书。也许它可以帮助我了解如何管理这样的模型抽象。

我觉得这个问题与DDD没有太大关系,但我正试图回答它;-)

我肯定会将数据库对象转换为域对象。原因很简单:如果您更改了底层数据存储,您的对象将发生更改,然后您将不得不出于技术原因更新域代码

除此之外,数据库对象包含的内容确实比域感兴趣的内容要多。这是摆脱它们的另一个原因:对域对象进行建模,使它们干净,并按照您的期望进行操作

此外,您希望能够轻松地测试域对象。如果它们包含了所有的数据库内容,这将使它变得不必要的困难


长话短说:不要在域层中使用数据库对象。

我认为这个问题涉及很多DDD。
你为之奋斗的是聚合。使用nosql数据库时,文档应包含聚合。
在同一个聚合中为实体使用不同的文档将产生持久化的效果。聚合对应于存储的多个文档,这可能会独立失败(导致数据损坏)。
DDD聚合是域的事务边界,聚合的持久化必须是原子操作

关于技术方面的东西,如果需要,请从域模型中删除它们是好的是干净的等等。
但是,如果离开它们会导致更多问题,那么就找到一种方法将它们隐藏在域之外。因此,这取决于该领域的规模


有关更多信息,这可能会很有帮助:

谢谢你的回答,这就是我一直在等待的:)谢谢你的回答。我对实体使用不同的文档,因为例如,其他实体可能与项目有关系,所以我希望有自己的文档id引用。但你是对的,数据一致性必须在代码中处理。我还希望实体存储在不同的数据库中。因此,聚合的持久化在我的业务逻辑层中是一个原子操作,但在我的数据库网关层中是多个操作。“在我的业务逻辑层中是一个原子操作,但在我的数据库网关层中是多个操作”是的,这就是问题所在,DDD中聚合的主要用途是,在保存它时,可以确保保存整个聚合。有多个操作可能会导致数据不一致(存储3个实体,第一个实体后数据库崩溃)。如果无法避免,请使用包含单个实体的聚合,但在多个文档中保存聚合时不要信任一致性。但这是否意味着聚合建模取决于数据的存储方式?与其说是模型,不如说是实体如何链接在一起。聚合的目的是管理数据修改的并发性。因此,您可以根据需要对实体进行建模,但当您将它们链接在一起时,您将使用id或整个数据结构创建聚合边界(如两个示例所示)。当您引用具有id的另一个实体时,您正在聚合边界之外创建链接。最后一件事是“我对实体使用不同的文档,因为例如,其他实体可能与项目有关系,所以我希望有自己的文档id引用”。您用来将它们链接在一起的是实体id,而不是文档id,区别在于后者依赖于db实现,但您的模型不应该依赖于所使用的存储技术。存储模型应适合您的域模型。因此,如果您的实体位于聚合中,则应参考聚合id和(如果需要)聚合中实体id。如果您负担不起这本书,您可以随时阅读Martin Fowler关于DDD聚合的内容: