fREWdiculous!
17 Jun
Okay, I got some responses based on my question yesterday about why validation shouldn’t be in the model of an MVC-based app.
This is what I got out of the responses:
This means that if you have some kind of time based input the timezone modifications need to happen in the controller. Or the even better example is that sometimes a user can change more of a model than another user based on the user’s permissions or roles. Personally I would just have a method on the model that had specific columns enabled and disabled, and then it would throw exceptions based on what columns the user tried to set. I can’t say for sure whether or not that is a scalable methodology. But it seems reasonable.
General programming practices often say that you should fail fast. This makes sense because you then have less time for bad data to be in the system. The problem is that assumes that you validate your data in the controller. If your model does validation, (again, assuming you validate at all) you should never be able to have bad models. But if you create some model at more than one place in your Controller, then you have to validate in both. I personally don’t think that this is a good argument.
Basically the issue here is that models shouldn’t return error messages that the user reads. The reason being that the model doesn’t know what language the user speaks, or in what context the user will read the message, and therefore, can’t give the user the correct language error message. So instead the controller gets some kind of error code from the model and translates that into an error message. Not really a hard thing to do, but certainly something to keep in mind.
And then there is the other end of the spectrum, which says that you really shouldn’t validate in your database or your model. I can see why that is really the best solution, but I think it needlessly ties a lot of dependencies on your specific database. Good luck writing a database independent trigger (or whatever) that ensures that a given input is an email address. Or a valid url or even something more complex. Honestly, I worked on a project like this in college, and it was nice that we always knew for a fact that our application wasn’t saving bad data because the DB wouldn’t let it, but we also had one guy who just did database coding. I like Perl. I like Javascript. I use DBIx::Class because I am happy to not write direct SQL. Writing code in the DB to validate my inputs just sounds painful.
I have decided that I am going to try to integration my validation with my models. I know there are places where I will have to do validation in the controller, and that I also need to have the controller relay the messages from the model to the user, but it still seems to me that the controller doing validation should be the exception rather than the rule.
5 Responses for "Why you should validate in your controllers and not your models"
(Sorry, my english is not so good)
You should definitly validate in your model and not in your Controller. The points thats sums all this up are no arguments for that.
1) Models don’t know about user
Yes, this is right but models should not know anything about users. You can think of a model as a simple class that do your abstraction for your data. And if you have a person database and a field “email” then you should do the validation in the model. If the “user”, let’s say in a webapplication, can edit the field is out of the scoope from the model. The user exists only in your application, not in your model. And you can use your Model more often. Let’s say you write some shell scripts where you can just populate your database. You use your model, but you don’t have a user.
To say it simple. validation of data should be in the model. The checks if a user can modify the data should be in the application where you use your model. And the check if a user can modify a piece of data is not any data validation at all.
The point with the locale is the same. Your model just saves data and checks if all data is correct. If you use locales in your application than formating data goes in your application the controller. But the validation goes in your model. Because not every application uses locales. Or another point you have the same model but different interfaces. Anybody hear about REST?
If you do validation in the controller then you break DRY.
Often data are CRUD. And in CRUD you have at least 2 points where you need validation. in CREATE you need validation and in UPDATE you need validation. You have a REST Interface? This adds creating and updating over rest. You have shell scripts? This adds another two points for validation. So you end with 6 points where you validate your data. This is stupid!
Everytime you change or create data you need to validate your data. Everytime you create or update your data over the model you need to check if your data are valid. So what is the point to don’t do it in your model?
Why should it be better to always repeat yourself?
And the other point. You need the model for some validation. Lets say you want to add a new user. How do you check without the model if the new user that you create with a username does not already exists?
2) The earlier you validate the better, or fail fast
The point is even wrong. You should not fail at fast as possible, you should fail at the point where it is correct.
Let’s say you write a programm that opens some files and need to write new files.
If you keep this approach, what did you do? At the start at you program you do just the checks if all 4 files exists? With a “if ( !-f $file1) { die … } “?
Everybody will say that this is not correct and that this have Race Conditions. Who says that at the point where you later open the file, the file was not deleted by another program/user?
The only correct way is to open and to fail when you open the file. Not to do the check before.
The same goes for a webapplication. You create a new user. Okay you do your checks in the controller? You have a little function that return if the user already exists? Nice. But you hardly fail.
Who says that if you check if the user don’t exists, and then “AFTER” this check you add the user, who says the user was not added by another person?
A webapplication itself is a multiuser application by design, so a check before adding the data is completly wortless. Here you have the same Race Conditons with the file opening example. The only correct way is to add the user. And check if the adding returns a “duplicated key” entry.
Sure you can add the checking of a user twice in the controller and the model if you like, but why? Here you just do Repeat yourself.
The Point is not do the validation and failing at fast as possible. The point is, to validate and fail at the right place, and sure and then fail at fast as possible.
3) If you do decide to do model based validation you need a structure to deal with that
The issue here and what you describe are correct, but your solution that your describe are incorrect.
The point that you hear speak is in my opionion the greatest point where Perl itself fails, and the whole Perl Community still fails at all.
It is correct that if you return a error message you don’t have the language of the user and the whole context etc. but the point is. Returning a error message is the whole problem.
“RETURNING” is the complete wrong approach and the right way is to throw “Exceptions”.
This does a big difference. Because if you use Exceptions that are real objects (that point is needed) you can then put your data into this object.
Lets say you write a catalyst applicataion then you would write something like this:
use TryCatch;
try {
$c->model(‘DB::User’)->create($data_href);
}
catch ( Your::Schema::User::Duplicate $e ) {
$c->stash->{template} = ‘error.tt’;
$c->stash->{error} = sprintf(q{The User (%s) already exists}, $e->user);
$c->detach();
}
The Point is, nobody really uses Exception Classes and if there use a Exception Object, mostly there put not data in the objects. This is a huge Fail for Perl and the Perl Community itself!
DBIx::Class returns a exception object, but from the object itselt you don’t know which error occured. Every error should have his own Class! In the example above with the catch() i catch Duplicated entry errors, but this is not something you can use directly. Because Error Handling in DBIx::Class fails.
If all would use this approach correct you don’t have the problems at all. At the moment i use this approach, but the worse thing is i always need to build this error classes for all cases by myself. The most Perl Modules out there don’t provide something like this.
And if you do this, your a really more productive. My Controller just often looks like
sub create_something {
eval { #model(‘ABC’)->create({…})
};
$c->error($@) if ($@); #error() automatically then i could even left the “sucks” code.
And where is the other code? The validation is in the model, this throws exception if something is invalid. And i added a plugin that hooks into “finalize_error”.
And there i only write for every error only ONE! Handling. And i have this error handling in the whole application everywhere!
4) Do all validation in the database
I don’t have to say anything against it. The only problem that can raise with this is, that you lost the flexibility to easylie change the database.
And i thing this is sometimes harder. How do you check in the database that something is a email Address?
Okay some Oracle or PostgreSQL can possibly do it? But what is if you use crap databases like MySQL?
BTW, more advanced databases like Postgresql allow you to make custom types so that you can validate deep in the database things like email addresses, etc. Just if you get that far before catching it that will generate a server error, and is hard to catch.
Realistically, a solid app ends up having to validate in more than one place.
If you don’t include validation in your database (proper types, keys, and constraints) you risk data corruption. If your database becomes corrupt, it can be very difficult to recover without simply throwing data out.
Typically, you’ll also end up needing to do some validation in your model classes too. Some of this validation can be auto-generated from introspecting the database. But you do it in your model because it gives you a chance to control the content of error messages.
Often, you also have complex validations that current SQL databases make difficult or impossible to implement (like validations of state across multiple tables) and so you implement this in your model too.
This is an unfortunate situation, but the alternatives are even worse. Having a dumb database is a recipe for disaster in the future.
Trying to do all validation in the database would be appealing if the database had good support for complex constraints, but SQL is horribly broken in this regard.
Hi,
I started to write a response here but it got pretty large so I copy and pasted it into a new article.
http://www.simplicidade.org/notes/archives/2009/06/where_to_put_yo.html
Thanks,
FYI, I have begun to repent of my DB-heavy days. I still think there is value in having a DB with real foreign-key validation (InnoDB, Postgres, etc), but I no longer think that higher-level business logic belongs down at the DB level. Two reasons:
1) It’s hard to write. You saw how things went with TOME. I was really the only one who understood the validation logic (because I wrote it), and it was confusing even for me. Expressing validation in Ruby or Perl tends to be much easier to understand and modify.
2) It doesn’t scale. Putting tons of business logic in the DB makes sharding difficult or impossible. Also, all your INSERTs are (potentially) blocked on a single validation thread. It’s easy to add lots more app server instances, but if your DB is maxed out, and you can’t thread out your validation, you’re stuck.
I’m starting to use some of these ideas on one project I’m working on–I’m moving to a schema that’s basically two columns: id and serialized blob. I’m not done with it all yet, but at this point, the performance implications are impressive.
A lot of my inspiration for all this change came from watching this video: http://www.infoq.com/presentations/Facebook-Software-Stack I highly recommend it.
Leave a reply