如何在CouchDB中实现唯一键约束

3
我使用CouchDB,并希望所有用户都有唯一的电子邮件地址。我期望当我尝试复制电子邮件时,数据库返回状态400(错误请求)。
但是由于在CouchDB中没有定义约束的方法,所以我应该自己实现它,我的问题是:
这个规则应该在我的应用程序的哪个层次中?
(1)领域对象层 我不知道如何在此层中实现它。
(2)交互器层 可以在此处实现此约束,因为业务规则位于此处。但如果单个文档有多个规则,则可能会增加不必要的复杂性...
function createUser(userData)  {
  let email = userData.email;
  let exist = await userDB.userExist(email);

  if(exist) {
    // return status 400
  } else {
    // create user
  }    
}

(3) 数据库网关层
这种限制也可以在数据库网关层中实现。通常,我们会为每个特定的实体设置一个网关。但是这是否意味着外部服务适配器包含一些业务逻辑?

class userDB()  {
  constructor(opts) {
    this.db = opts.db.connect();
  }

  async userExist(email) {
    return await this.db.fetchByView('email', email);
  }

  async create(email) {
    let exist = await this.userExist(data.email);
    if(exist) {
      // throw error
    } else {
      // create the user
    }
  }
}

你可以在CouchDB中使用valide_doc_update。每当文档被创建/更新时,该函数将被调用。通过在CouchDB中添加这些验证或在CouchDB上方添加一个门面,这使得您可以轻松地在多个平台上维护业务规则。 - Alexis Côté
@AlexisCôté,“valide_doc_update”无法以并发安全的方式强制执行唯一电子邮件地址约束。 - Constantin Galbenu
谢谢,我会检查的 :) - Simon Bruneaud
@ConstantinGalbenu 将电子邮件作为 _id 是您可以拥有的最安全的事情。如果使用已存在的电子邮件创建用户,如果没有传递 _rev,则会出现冲突错误。即使有某种形式的复制,这也不会是并发安全的。另一种解决方案可能是在数据库之上只有另一个 API 来创建用户。您可以拥有一个队列来创建用户并验证其电子邮件。 - Alexis Côté
我给出了一个简单的例子,但在现实世界中,我的应用程序需要唯一的电子邮件地址以及用户名和可能的其他字段。 - Simon Bruneaud
2个回答

2

唯一的电子邮件地址是一个非常古老的DDD主题。它与集合验证有关。 最简单的(也是我认为最好的)方法是在数据库级别放置约束。

据我所知,CouchDB中创建唯一约束的唯一方法是使用_id字段,因此您可以使用此解决方案。这个想法是将电子邮件放入_id字段中。

let exist = await this.userExist(data.email);

if(exist) { // throw error } else { // create the user }

这种方法对于并发更新来说并不安全。两个用户可以同时创建。想象一下,对于两个请求,this.userExist(data.email)都返回false。


我认为我应该保留Couchdb创建的默认'_id'(uuid)键,因为我想使用该_id处理关系,如果用户想更改他的电子邮件地址,这可能会成为一个问题。但是我肯定会在数据库层面处理约束。谢谢。 - Simon Bruneaud
@SimonBruneaud 另一个解决方案是创建一个仅用于电子邮件地址到用户分配的集合,并在那里将 _id 设置为 email address。换句话说,您将拥有一个 EmailAddressToUserAllocation 聚合。 - Constantin Galbenu

1
我不是CouchDB专家,但从纯净架构的角度来看,确保唯一电子邮件地址是一个应该在交互器中实现的业务规则。
采用这种方法,即使您决定将详细的CouchDB替换为其他详细信息 - 另一个存储系统,您的业务规则仍将保持完整。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接