Where Should Server-Side Form Validation Go?
Posted on Oct 10, 2007
I am working on some internal training at my day job on using object-oriented programming best practices in ColdFusion as well as Mach-II. As part of this, I am building a sample application based upon my Objects and Composition - No Framework tutorial. This includes a number of forms that all interact to allow you to create the example "Xbox console" objects that I used in my example. Its funny when you are trying to build an application for the real-world, you often go with the solution that is expedient and works but when it is for teaching purposes, you might prefer to actually confirm that this is the (or a) "right way" to do things.
One question that I am trying to get a handle on is where is the best place to put server-side form validation. The most common place I have seen recommended by several prominent folks within the community is that validation should live within the bean. Along these lines my generator actually does generate a validate() method. Others have also looked into this topic lately. Jeff Chastain of Alagad wrote several entries on the topic earlier this year. His posts focused on a more generic server-side validation framework. Clearly there is no single answer to this question. However, I am finding the more I think about it, I don't like either of these solutions. Let me explain and then discuss where I have settled, at least for the moment.Server-side Validation in the Bean
Let me start by saying that I do think that a validate() method in your bean is useful, particularly to make sure a bean is valid before sending it off to be persisted. However, my current thinking is that this should not be used as your form validation. First, it assumes that somehow your form, which is obviously part of your UI, is somehow tied to a specific implementation of an object within your model. This clearly isn't always (or perhaps even rarely) the case. For instance, let's take a multi-step form for user information, all or portions of which may make up my user object. In this case, you can't validate the data from step 1 against the validate() method of your user object because it would clearly be incomplete. However, you would want to provide validation feedback to your user at each step. As a second example, let's take the common "confirm password" field in a form. This field actually has absolutely nothing to do with your data within your model. Clearly you aren't going to put validation for the "confirm password" field in your bean. Form validation, in the end, seems like part of the UI to me, even if it is occurring on the server. (for the sake of this discussion, I am not taking client-side validation into account, since, in the end, some combination of both client and server side validation are required).
Server-side Validation via a Generic Validation Framework
The concept of a validation framework that Jeff and others propose (some so already exist) seemed intriguing to me at first. I like the idea of being able to re-use a number of pre-baked rules throughout my applications. However, once you go down this path, you need to configure the framework, which means learning how to configure it, and so on (Jeff's idea seemed to be to create an XML config). In the end, I can't get past how you aren't still building, in one manner or another, a specific validation pattern to match a specific form. To me it seems that the one form to one validation scheme is unavoidable, since you are validating a specific implementation of a specific form in a particular UI (for example, your Flex UI form may differ from your HTML one which may further differ from a mobile version and so on even if the resulting data in your model may be the same).
My conclusion is really just a couple of questions: 1) do you agree with the concerns/criticisms above? 2) regardless of your answer to one, how do you handle server side form validation? Right now I have settled on building server-side form validation in filters within Mach-II. While, in my current implementation, this means that I need a new filter for each form, as I said before, I can't get past that a one form to one validation scheme pattern is, in general, a necessity. Although filters are not technically part of the UI, they seem a better place to put the form validation code than in the model, where, as I said before, it just doesn't seem relevant. So, am I off my rocker or over-thinking the issue? Thoughts?
I've been pondering the same issue to some extent lately. I've toyed with the idea of putting the logic in my controller (MG) before I make the call to persist the data - but if you're using a remote facade to interact with your model you'll be bypassing the controller.... I'm interested to see what others think.
Posted By todd sharp / Posted on 10/10/2007 at 8:11 AM
Well, I'm with you, I think. Last time, I put the validate function in my bean, and it became obvious that it didn't belong there. One reason is that my BOs were Transfer ORM decorators, and there were idiomatic problems with validation in that context, IIRC. Further a form != a BO, at least not necessarily. I think each form needs its own validation (though common bits could be inherited), since there might be a few different forms for given data.
What irks me is that I usually have to repeat (form field) label names in my validation, which violates DRY. That's one thing I do like about Struts validation--one can have their labels defined in one place.
I think validation isn't Model code. It seems like it's mostly UI and Controller.
Posted By Jamie Jackson / Posted on 10/10/2007 at 8:12 AM
My thinking would follow the lines that your object should validate itself. If you are working on a multi-part form you may need to figure out a way to track which section of the form you are on, but there should be a way for the object to say whether the data contained therein is correct.
The confirm password should not be in there and IMHO should be front-end only as it is just a way to help the user not mess up their password by way of mis-typing.
Food for thought would be to come up with a way to customize the validation by passing a list of the attributes you want to validate (blank for all) and let your validation function check only those attributes you tell it to. That would help solve the problem of multiple part forms to some extent since you could tell it to validate the attributes that were on that part of the form.
Posted By Randy Merrill / Posted on 10/10/2007 at 8:12 AM
Posted By Jamie Jackson / Posted on 10/10/2007 at 8:19 AM
I've argued (with myself) in circles about this for years now, and it's one of those times where the only answer I've ever come to that I didn't disagree with was "it depends."
In case where there's a special form that needs validation that's focused on its UI elements that don't line up with model elements (and shouldn't permeate the model), I'd like to keep validation in the controller layer. A signup form requiring the user to enter their password twice is a good example.
However, in other situations where I'm writing software that's to be consumed by multiple clients (multiple HTML forms editing parts of the same model entities, Web services, or Flex), I don't want to duplicate the validation and I'd like to make sure values sent by remote entities (like Flex) are valid. In that case, I need something, somewhere in my model / service tier that validates.
I'm also often in situations where whether or not something is "valid" may have multiple definitions, based on workflow or other context.
The compromise I often use is to remove validation from both the model and the controller tiers, isolating it in IValidationRule implementations. It's a really generic interface that takes a struct (or CFC) in its validate() method and returns a ValidationErrorCollection (part of Model-Glue, but easy to rip out and use independently).
My controller can do the equivalent of ruleFactory.createRule("signupFormValid").validate(FORM), and my user can do ruleFactory.createRule("userValidForPersistence").validate(this).
Posted By Joe Rinehart / Posted on 10/10/2007 at 8:28 AM
In my opinion, it depends on what is the nature of the validation? Is it validating basic properties that are required by the make up of the bean object or is based on current business rules? Say I have a car object and in order to call myCar.accelerate() I need to first make sure it has tires then this validation belongs to the bean since the car won't go anywhere without rolling on something. If my application dictates that I cannot run the car unless there's a gorgeous brunette on the passenger side then I'd leave that validation on my service layer.
my 2 cents..
Posted By phill.nacelli / Posted on 10/10/2007 at 8:29 AM
I was going to suggest something along the lines of what Randy mentions about passing a list of attributes that require validation to a composed validation object within a bean and use 'contextual' validation method names like, isValidForSave() [validates all properties] or isValidForLogin() [validates login credentials]. This contextual style of validation is exemplified in Joe's approach which I like a lot.
I'd like to add another question to the mix. At what stage in a request process do you validate? Knowing that web service calls will bypass the controller layer, I tend to validate within a service.
In the end, I think it's up to the developer to engineer a system that works for them. Building the perfect validation scheme is like building a better mousetrap. A lot of thought has gone into schemes in the past (custom tags, includes, etc.) and lot more will go on in the future...
Posted By Paul Marcotte / Posted on 10/10/2007 at 8:58 AM
I use the page controller model for handling validation in non-framework apps which seems to work very well for multi-stepped forms. In MG:U I have started using a validation "UI" Controller for all form submissions that either allows the "submit" event to be broadcast to a model/business controller or returns to the form with an error. I believe that validation is part of the flow process and by definition belongs in the Controller layer. Just my opinion, if this is way off base please let me know, as I am always looking to learn a better way!
Posted By Robb / Posted on 10/10/2007 at 9:25 AM
good point about bypass controller layer.. that's why I strongly agree with using Service Layer and let your controller tap into that.. same with your web services.. Doug Hughes had a good write up on this topic (http://www.phillnacelli.net/blog/index.cfm/2007/4/18/Doug-Hughes-on-Service-Layer)...
Posted By phill.nacelli / Posted on 10/10/2007 at 9:41 AM
Thanks for the reference and link back. The "framework" (I am not sure it is really enough to be considered a full framework) that was proposed and discussed in those articles is ready to be opened up with an Alpha version as soon as we get the access issue to our SVN repository fixed.
As a general overview though, it is broken up into 3 parts ... a collection of pluggable validators, one or more data transformers and one or more data set configurations.
The validators allow you to build your own custom validation rules. This could be as simple as checking that a given data field (element) contains a value or as complex as running tests based upon multiple data fields, talking to a database or web service, etc. The idea is that no validation framework can imagine every possible test, so this allows you to easily add your own validation routines.
The data transformers abstract out the concept of where the data being validated is coming from. This way, we can have a simple form structure transformer that knows to retrieve data out of the form scope and send it to the validation engine. We also have a basic bean transformer that inspects a bean and grabs its data based upon each getter method. The goal here again is abstracting out where the data is coming from so if you prefer to validate beans vs. form data, you can do whatever you wish.
The last piece to the puzzle is the data set configuration. Basically what this means is a mapping setup between data elements and validation rules. For example, the firstName data element requires the "required" valdiator to be run. Through this mapping your can also setup mappings between groups of fields - for example a validation rule for the firstName, lastName, and emailAddress data elements to determine if they represent a unique user. This mapping can be setup via an XML configuration or via a programmatic API if you want to store and manage the rules via your application (a dynamically built form for example).
That is enough of a preview, but this code should be available in Alpha form very shortly with documentation and a Beta to follow close behind.
Posted By Jeff Chastain / Posted on 10/10/2007 at 10:50 AM
when I wrote ICEGen I put this into the model in a validate method. For me it's just alot easier to have everything in one place then having 10 different components. So if I want to validate a form, I just do:
<cfset obj = createobject("component", "_model.dao.Users").init()
<cfset ec = obj.validate()>
obj.setMomento() takes a structure and maps it to the set methods in the class.
obj.validate() returns a structure of errors.
Posted By tony petruzzi / Posted on 10/10/2007 at 10:59 AM
1. In the domain object. The domain object should be able to say "as far as I can tell, I am valid/invalid". This is defensive programming to avoid bad data, and should throw an exception. You might put in an AOP interceptor to make this happen automatically before persistence.
2. In the command object. The SaveUserCommand object should be able to say "I have enough data to execute, and the data looks good to me". This is where one would check unique constraints etc. Once again this could throw an exception, but it becomes debatable above this layer whether to use exceptions or return values.
3. In the service object. The service should throw a validation error if it is not able to create the command object.
4. In the controller. The controller should be able to say "this form is valid by whatever rules I want to apply to it". These rules may temporarily be more lax than required by the service/domain, but before the final commit must boil down to essentially the same thing.
Now, a some points to note about this scheme:
. Each layer may be vanishingly thin. In the simplest case, a CRUD form that maps directly onto a persistable domain object and thence to a table, the service and command layers might disappear and the controller may choose to just catch the domain object's exception.
. Because the controller must ultimately satisfy, the lower layers, it would nice and DRY if the controller could query the lower layers about what rules they intend to apply. The controller can then apply the same rules, but use UI-specific field names and messages. The controller can also then reflect those rules back to the UI in the form of JS routines. I think it's basically this re-use of rules that the various frameworks are trying to achieve.
. With the full stack in place, a single constraint might be checked four or five times. One might want an escape mechanism to prevent expensive checks being repeated, but in general this is a strength. It allows the system to put concurrency management at the appropriate level. For big scary operations, one can put a lock around the whole conversation. For more mundane operations, locking can be right down at the command level.
. How one expresses the rule set - in code, in XML, as a method within the object, as a composed validator etc I think is a matter of preference. I think there's a basic philosophical divide between whether one thinks validation part of a domain object's core concerns and therefore belongs in its interface, or whether it is not and therefore would reduce coherency.
Posted By Jaime Metcher / Posted on 10/10/2007 at 2:08 PM