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

How to implement unique key constraints in CouchDB

我使用 CouchDB,我希望我的所有用户都拥有唯一的电子邮件。当我尝试复制电子邮件时,我希望数据库 return 状态为 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
    }
  }
}

Unique email address 是一个非常古老的 DDD 主题。它涉及到set validation。 最简单的(在我看来也是最好的)是在数据库级别设置约束。

据我所知,在 CouchDB 中创建唯一约束的唯一方法是使用 _id 字段,这样您就可以使用 this solution。这个思路是把email放在_id字段中。

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

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

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

我不是 CouchDB 专家,但从纯清洁架构的角度来看,确保唯一的电子邮件地址是一项业务规则,应该在交互器中实施。

使用这种方法,即使您曾经决定用其他细节(另一个存储系统)替换细节 CouchDB,您的业务规则也将保持不变。