2015-08-15 4 views
2

Предположим, что у меня есть супер простая регистрация пользователей, чтобы электронная почта пользователя была уникальной для всех пользователей.Как отделить проверку уникальности от слоя сохранения?

Я выразил это требование в таких функциях.

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

(defn is-unique? [email] 
    (not (db-api/user-exists {:email email}))) 

Но я хочу отделить мою проверку от базы данных, я хочу сделать ее чисто функциональной. Я мог бы, вероятно, также впрыскивать базы данных API в качестве параметра для validate-user, как

(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. Похоже, что эта зависимость подрывает всю концепцию отделения уровня бизнес-логики от уровня сохранения. Поэтому я ищу мышление, в котором объясняется, как это сделать правильно или почему это невозможно.

+0

Вы собираетесь ввести пользователя впоследствии? Если да, то с этим подходом у вас будет состояние гонки. (Если вы не держите все электронные письма в памяти и заблокированы). – ClojureMostly

+0

Вы разработчик Ruby, поэтому я должен напомнить вам о [этом разделе руководства Rails по проверке уникальности] (http://guides.rubyonrails.org/active_record_validations.html#uniqueness). Часть этого раздела по-прежнему применяется в Clojure. –

ответ

2

Чтобы избежать условий гонки, база данных должна справиться с этим ограничением. Вероятно, вы ищете эквивалент INSERT IF NOT EXISTS в мире SQL.

На практике у вас может быть функция create-user и функция update-user. Функция create-user может использовать проверку IF NOT EXISTS.

Вы не можете отделить его от базы данных: ответственность за сохранение ограничений данных (отношений, в реляционном мире) лежит на базе данных. Никто другой, кроме базы данных, не может этого сделать из-за условий гонки.

Позвольте мне остановиться на том, что на примере:

Предположим, что два пользователя (ПользовательА и USERB) желают в то же самое время, чтобы создать учетную запись с новой электронной почты уже не выбранного: «[email protected] ». то Ваша система запрашивает базу данных:

  • проверки, что «[email protected]» не существует от имени ПользовательА, он возвращает истинное

  • проверки того, что «[email protected]» Безразлично «т существует от имени USERB, он возвращает истину

Вы переходите к:

  • создать учетную запись для ПользовательА с почтой «[email protected]»

  • создать учетную запись для USERB с почтой «[email protected]»

В зависимости от семантики базы данных и ваши запросы, вы можете:

  • две учетные записи с почтой «[email protected]» (напр.никаких ограничений в базе данных, не уникальный индекс)

  • счет для «USERB» не создан, нет счета для ПользовательА (напр. запрос на создание учетной записи для ПользовательА не удалось, потому что был уже мал присутствует)

  • создана учетная запись для «userB», тогда данные для «userA» переопределяют данные из «userB» (например, семантика оператора обновления - это пользователь в базе данных - возможно, ошибка на этом этапе)

Из-за этого вы должны попытаться создать учетную запись и сообщить базе данных, если она не удалась. Вы просто не можете проверить это самостоятельно, не воссоздавая свойства базы данных самостоятельно (я лично не осмелился бы попробовать).

Смежные вопросы