Of course you can still use AR validations, that is not my point.
But data consistency related validations should not trust AR for that, for example, if you have a “unique” constraint, and you are just trusting the database, it is really easy to break the DB consistency if for some reason you have a long transaction (long varies according to your concurrency, infrastructure, …)
For example, lets assume the following scenario:
You have a User model with a unique constraint on the username, and every time a new user is added to the system you need to send a validation email, the “create user” transaction is only completed after the email is sent.
Today, the email delivery system is overloaded for some random reason and email deliveries are taking 2 seconds, instead of the usual few milliseconds.
Your user model would be something like this:
class User < ActiveRecord::Base validates username: uniqueness: true end
Your database script would be something like:
create_table :users do |t| t.string :username end
And you have this transaction:
User.transaction do @user = User.create! params[:username] UserEmail.signup_email(@user).deliver! end
This code usually works, but since today your email delivery server is under heavy load, and you are very unlucky, because two john.doe just clicked “Signup” within 1 second of each other.
Now you have two duplicated usernames in your system.
But if your migration was:
create_table :users do |t| t.string :username t.index :username, unique: true end
You would have no problem at all, because the database system can handle it very well.
But what is the problem with the first code?
It is simple, inside the transaction, when rails query for the data checking if there is no conflict for the username field, the data from the other transaction is not there yet.
But when the database has the validation, and the data is inserted in one transaction, the other transaction that is trying to write to the same “key” is locked and needs to wait, so the data is always valid.
This of course will not happen for small transactions with only one statement, but once you need a bigger transaction you are in trouble if you kept your validations only in the AR model.