Clojure 如何将唯一性验证与持久性层解耦?

Clojure 如何将唯一性验证与持久性层解耦?,clojure,coding-style,Clojure,Coding Style,假设我有一个超级简单的用户注册检查,用户的电子邮件在所有用户中必须是唯一的 我已经在这些函数中表达了这个要求 (defn validate-user [user] (and (:email user) (is-unique? (:email user)))) (defn is-unique? [email] (not (db-api/user-exists {:email email}))) 但是我想将我的验证从数据库中分离出来,我想让它完全起作用。我可能还可以将数据库API作为参数

假设我有一个超级简单的用户注册检查,用户的电子邮件在所有用户中必须是唯一的

我已经在这些函数中表达了这个要求

(defn validate-user [user]
  (and (:email user) (is-unique? (:email user))))

(defn is-unique? [email]
  (not (db-api/user-exists {:email email})))
但是我想将我的验证从数据库中分离出来,我想让它完全起作用。我可能还可以将数据库API作为参数注入到
验证用户
,如

(defn validate-user [db-api user]
  (and (:email user) (is-unique? db-api (:email user))))

(defn is-unique? [db-api email]
  (not ((:user-exists db-api) {:email email})))
但我不知道这是否是惯用语


而且,它感觉到
validate user
的使用者不应该关心数据库api。这种依赖性似乎破坏了将业务逻辑层与持久性层分离的整个概念。因此,我正在寻找一种心态来解释如何正确地做到这一点,或者为什么不能做到这一点。

为了避免竞争条件,数据库应该处理这个约束。如果在
SQL
世界中不存在
INSERT
,那么您可能正在寻找它的等价物

实际上,您可以使用函数
创建用户
和函数
更新用户
创建用户
功能可以使用
如果不存在
检查

您无法将其与数据库分离:维护数据约束(关系世界中的关系)是数据库的责任。由于竞争条件的原因,除了数据库之外,没有人能做到这一点

让我用一个例子来说明这一点:

假设两个用户(userA和userB)希望同时创建一个帐户,但尚未选择新的电子邮件:user@example.com". 然后,系统查询数据库:

  • 检查“user@example.com“不代表userA存在,它返回true

  • 检查“user@example.com“不代表userB存在,它返回true

然后,继续执行以下操作:

  • 使用“邮件”为userA创建帐户user@example.com"

  • 使用“邮件”为userB创建帐户user@example.com"

根据数据库的语义和您的请求,您可能会得到:

  • “两个邮件帐户”user@example.com“(例如,对数据库没有约束,没有唯一索引)

  • 为“userB”创建了一个帐户,但没有为userA创建帐户(例如,为userA创建帐户的请求失败,因为已经存在错误)

  • 创建了一个“userB”帐户,然后“userA”的数据覆盖了“userB”中的数据(例如,update语句的语义是数据库中的user-此时可能是一个bug)


因此,您应该尝试创建一个帐户,并让数据库告诉您它是否失败。如果不自己重新创建数据库的属性(我个人不敢尝试),您就无法自己进行检查。

以后是否要插入用户?如果是,那么你将有一个比赛条件与此方法。(除非你把所有的电子邮件都存储在内存中并锁定)。你是一个Ruby开发者,所以我想我应该提醒你。本节的部分内容仍适用于Clojure。