More On Data Validation - Translating Error Messages
Posted May 8, 2008 at 9:13 AM by Ben Nadel
I am still trying to wrap my head around things like MVC (model-view-controller) and OOP (object oriented programming) and cf.Objective() has really brought that back into the spotlight. One of the largest road blocks in my journey to understanding it all is the processing of data validation and reporting errors. I gather that a smart bean will usually have some sort of Validate() method that probably takes some sort of error collection, populates it with data validation errors, and then returns it. I have messed around this type of methodology and I usually have my bean or my controller add error messages like,
"Please enter a valid email address."
The problem with this methodology, as I am starting to see, is that it creates a tight coupling between the View and the Model. By asking the Model to generate error messages that are View specific, it destroys a huge benefit of MVC, right?
Think about the use case that I have in my head - a login system in which all usernames are comprised of people's email addresses. Because of this, the security service that is used to log people in requires a valid email address and a password. Now, let's say you have two different interfaces for this application: a standard ColdFusion / HTML interface and a FLEX application. Now, let's say that in the HTML version of the login, we have field labels that read, "Username" and "Password", and in the FLEX application, we have fields that read, "Email" and "Password".
Can you already see where this is going? The HTML application, has chosen to call the login handle a "Username" and the FLEX application has chosen to call it "Email". Both of these are correct; the email address is a username and the HTML application has just chosen to be more general with the naming than the FLEX application. Now, if MVC allows us to be able to swap out our views, it means that we can't have error messages that are view-specific; otherwise, our HTML application, which asks for "Username" will present the error, "Please enter a valid email address," which would clearly be confusing to the user.
So, what do we take away from this scenario:
- The Model cannot create error messages as this would tightly couple it to the view. It can only create "field-specific" error flags (ex. User.FirstName).
- The field-specific errors must be translated into view-specific error messages.
Here's where I get much more lost. Where does this error-flag-to-message translation take place? Are the error flags passed off to the View and then the View is responsible for turning those into messages that make sense to the user? This seems like it makes the most sense as the View is really the only part of the whole application that can possible know what is going to make sense from a user experience standpoint.
Or, is this the job of the controller? I don't know enough about MVC to even be close to answering this. Going back to our example above, do the HTML and the FLEX applications use the same controller? I assume not as the FLEX application is using an application API which would probably get threaded through to a different part of the controller than the HTML version? Right? Or maybe I am way off on that. But, even if they are getting run through different parts of the controller, does this make it any easier? I don't think so - the error messages generated by the Model or Controller still cannot be interface specific. In fact, if an application is publishing an API, I think this becomes even more true! Any API, almost by definition, has to be more view-agnostic than anything else in the entire system as it deals with third party applications about which it cannot possibly know anything.
So, here's the vision I am building in my head:
| || || |
| || |
| || || |
Obliviously, the error flags to error message translation cannot happen between the controller and the view as there is nothing there, so I guess this really has to happen in the View.
Or, is it part of the "contract" between the View and the application that the error messages will be standard? If the Model says, "Please enter a valid email" and the HTML view says "Username", is the HTML view violating its contract and not upholding it's "agreement" to use the application? After all, who says the View can use whatever field labels it wants? In the real world, when you go into a store, it's agreed upon that you use money to pay for stuff - this agreement has nothing to do with how well you (the View) know the cashier (Application); there is simple an understanding that you both speak the language of the US Dollar.
I am very lost on this and I feel that almost no tutorials on OOP or MVC cover this. Why is that? I must be missing something as this seems like such a huge and crutial part of the picture.
What Other People Are Searching For
Looking forward to seeing how people handle this... I'm running into similar issues trying to use jQuery to do client side validation - but then I still have to do something server side...
Me too. I think this is one of the biggest yes least discussed constraints to an OOP / MVC style application. I think that once I have a better handle on this, a lot of the other stuff is going to fall into place. Of course, I could be totally daft as well.
Especially in a situation where more than one View layer may be consuming the app (CF and Flex), the model and controller need to work very closely to the API model, as you noted. In other words, it should be the Model's job to catch and note the error, but it's got to be fairly abstract: "I got an auth value for userName that is invalid." The Controller does nothing more than pass this into the error struct and return it to the view, whatever that view happens to be.
At that point, the View then makes whatever determination necessary, such as distinguishing the Username label vs the Email label for the "userName" field error. The other benefit of this loose coupling is that you may even handle the way errors get to the UI completely differently in each case.
Just my 2c.
Ok, so it is the view that does the translation. At first, I want to say that seems like a lot of work, but I don't think it will be. I think it will make things easier once I get into that mindset.
Have you looked at the resourceBundle.cfc package? I think there are some articles out there about using resource bundles in flex as well.
In your example, the flex and HTML views differ significantly because you have different field names. MVC isn't magical in that you can have completely different views and expect them to be plug and play compatible. The more you diverge, the more you'll need to change in the controller to be able to "translate" your flex fields and your HTML fields to the same model. I would keep the field the same name under the hood, just the label should change. In this case, you very much have a programming API that should be a contract. However, that says nothing about what the user sees as labels or errors.
In your case, I would try and handle the difference by having a HTML resource bundle and a flex resource bundle.
HTML.userfield = "Email Address"
HTML.userfield_missing_error = "Please enter a valid email"
Flex.userfield = "Username"
Flex.userfield_missing_error = "Please enter a valid username"
Ok, you are making extra work for yourself (somewhat), but then the view and model don't really care about each other. One of them knows how to use resourcebundles (or maybe both), but neither really care as long as they know how to get their correct strings.
You should think of it as a "English Flex" translation and an "English HTML" translation.
Just to be clear, I did not expect them to send different values, just have different labels. So, it's not like the HTML version would have sent FORM.username and the FLEX version would have sent URL.email. Both would have submitted an singularly names "email" field of some sort.
But, you raise a very good point as well - getting the form fields into the model. In my world, my form fields are all named like, "first_name". However, I assume many bean properties are like, "FirstName". Therefore, I can't simply dump my form into my bean (as I think some people tend to do). I would need to have code somewhere that is like:
BEAN.Set( "FirstName", FORM.first_name )
BEAN.Set( "LastName", FORM.last_name )
Just as an example. Now, this code is very clearly coupled to both the model and the view. Where does this happen? I assume this happens in the Controller as it's really the only other place that this could possibly happen (the Model is too late, and the View is what posts the form data).
So, in that case, you would need agnostic models, but view-aware Controllers. Which means, (and I think this is totally fine), your HTML form has to submit to a different part of the controller than the FLEX form.
You're right about having to handle the data-passing at the Controller level, which is exactly where Jeff was going (I think) with his note about making increasing changes to the Controller as your Views diverge. If you have substantially different View structures (for instance, different formfield names, not just labels), then you would need different Controllers or Controller events to handle the various Views. As Jeff pointed out, you would no longer be able to have a single API that would cleanly honor varied request structures.
On the other hand, if you can maintain consistent data-coupling across Views and only tweak them on the visuals (like labels, error messages, prompts, etc), then the MVC separation remains clean and gives you the benefits you're looking for: plumbing the Controller (and the Model) only once.
Yes, that was part of my point.
My other point was to check out the Resource Bundles. If you use them (or something similar), you only have one spot in your code that determines which bundle to use and your view (or model) don't care. They just need to know to use the bundle to get the text they need.
I think I am gonna go with the extra controller work. I don't like the idea of naming my form fields based on some model somewhere. Seems like trying to fit everything in one box. Plus, when you have situations like street_address. Maybe you can't just throw that in the Model. Maybe you have to do something like:
Contact.GetHomeAddress().SetStreetAddress( FORM.street_address )
If you wanna deal with that in a generic way, then you probably have to come up with all sorts of form naming conventions and automagic conversions on the server.
I am huge fan of explicit actions. I will play around. Thanks.
Keep in mind that Flex doesn't even talk to an HTML Controller, so doing this translation in the Controller isn't really very feasible. One option is to allow for different translations based on the client being used. This could be kept in the session scope, or (I believe) detected from information in the CGI scope or page context. Based on what view is being used, the validators in the model use the proper translation mechanism. I do this with XML but a database is also an option. The same idea applies to I18N, where different translations are applied depending on the language in use.
Flex apps also have their own validation, so in most cases a malformed value will never even make it to the server anyway. And in reality, I don't think there will usually be a significant differential in error text or field labeling between interfaces. Even in a case like that, if the field is labeled "email address" and an error comes back saying "Your user name must be a valid email address" and the field is highlighted, I think folks could figure out what that means.
True, FLEX talks to CFC, right? However, we might be building something with a RESTful API (which I think is template based, but I could be wrong). And, yeah, there wouldn't be much of a difference in the error messages.
However, since I am struggling to get my head around all of this stuff, I have to think about even the edge cases so I can see where the "best practices" might be and possible troubles down the road.
Now, you mention i18n. That hadn't even occurred to me. Sounds like a whole other nightmare I am not ready to think about :)
Yup, Flex talks to CFCs, but only for some sort of remote validation. In my userVO (in Flex), it should "know" what makes a valid e-mail address for itself, so I'll have a validate that will first validate locally (does this e-mail meet the standards for a valid e-mail address), if so, go to the next step, if not return an error (invalid e-mail). If it validates locally (in Flex), now validate remotely, pass the e-mail address off to my userVO CFC (or gateway, or service, whichever method you use) which will now verify that that e-mail address does not already exist in my dB. If it does, send back "invalid e-mail" with perhaps a reason why it failed, else, pass back an all clear. I will usually have some sort of errorVO, that can accept pass/fail and return a message or error code, and anything that may be useful to the calling page. This makes things easier when passing messages around.
Back to your original question though, I usually go with the view knowing how to handle the error that is returned. The controller would receive the errorVO, which would then call the specific error alert on the view to let the user know what had happened. This could be handled either with the error message or perhaps with the error code passed along in the errorVO. This way whether you are sending the error the HTML or Flex side of things, the controller would just pass the "e-mail validation failed" back to the view, which would then handle the display of it to the user. The controller wouldn't need to know any specifics of how the view wants to handle the error, but the view would know what to do with the error it received.
Also, I'm not sure why you wouldn't be able to use the same VO for the user example you posted (for HTML which has a username vs Flex which has e-mail). Wouldn't your user just have a username, which could essentially (as you state) be either a username of some sort or an e-mail (if userVO had an e-mail property, it would just match the username/e-mail field). This would still be saved the same in the dB, and would just be a data field. The view (both for HTML and Flex) would just display them differently (and know that the data was going to be different). The labels would just show differently, but they would both be usernames, even if one looked like an e-mail.
The HTML and the FLEX would have the same data it would be sored in the database the same. The issues is aligning the error message with the interface:
Review the following:
* Please enter a valid email address
Username: [ . . . . . . . . . ]
Password: [ . . . . . . . . . ] [submit]
Here, the "error" message is very unclear. There is no mention of an email address on the paoge. Remember, our users are really not that good at using our interfaces.
Correct, but in this instance, you would not be returning a validate on the e-mail address from the userVO, it would be a validate on the "username" property of the VO. The view would then know how to handle this result (for the view that had e-mail address as the username, it would state "you entered an invalid e-mail", and for the one with the username, it would state "you entered an invalid username"), but both would receive an "invalid username" from the userVO via the controller.
Ok, I see what you're saying. The translation from object-error to view-error is done in the userVO, right? So, yeah, in that case, you can just pass those around and all is good.
@Ben and Gareth: Why not do both? In my validation (which happens in the model just before the object is saved), I return a Result object which has a success flag, a success message, and an errors array. The error array contains the property name and the message.
So if the Flex interface had a form field named "username", the error would still flag "username" as the bad property and the message would be "User Name is not a valid email address". But if the form field was "email", the erro would still flag "username" as the bad property (since under the hood this is still the user name, the interface is just labeling it "email"), but the message would still be "User Name is not a valid email address". Since the error is still fairly clear (email) and the field can be highlighted, I don't see any real problem with this. But even if it is a big enough issue to require handling, one could just supply alternate validation messages based on the interface. That would be a good bit of extra work for this apparent edge case but, depending on how likely it is and how much of an issue it is, that could be another option.
Good post Ben. I struggled with the same question myself when I switched to programming in MVC OO with MachII. This is my thoughts on what I figured out. The point of any OO programming is to isolate the objects from each other so that changes don't break the application. In the example you mentioned, the display doesn't really matter, as long as you pass the required information for authentication, you have completed the contract. The contract says you need a valid email address to authenticate, then your error message should say so and the view programmer should program a pretty view that tells the user what he needs to do so it is consistent with the error messages. If the view is confusing, the fields are not labeled correctly, your text is yellow on white, whatever, you should be able to change the view and not break the application. I would think that if you have to jump through some hoops and put extra logic in your model to accommodate different views, you have broken encapsulation although Jeff Price addressed that well with some kind of data to label mapping with his HTML and Flex translation idea. The question is which would be easier? Change the label on the view, or write some translation objects to map your data to other peoples views? The first is quick and doesn't break anything, the second complicates things and introduces more complexity which means more possible points for bugs.
I think I did not explain myself well. I am not talking about changing the model in any way. I think the model should not know or care about anything using it. Model is out of the picture.
What I am trying to do is figure out what the "Best practice" is such that the Model can be view-agnostic and the views can still use is. As such, the validation messages have to either be created in the view or in the controller (but not in the Model).
I will post on some experimentation later today to help illustrate this.
Definitely. That's about what I'd put in my original message, but didn't add it in to my latest post :)
"I will usually have some sort of errorVO, that can accept pass/fail and return a message or error code, and anything that may be useful to the calling page."
I see what you mean, I got lost too deep in the specific example.
I've done both, and it depended on the situation. In some cases I just put validation in the model, ie smart bean with validate() function. But in other cases I have a special validation object for the special situation. I don't know though whether you would call these validation objects the model or part of the controller. I've had situations where based on roles or permissions you might want data validated one way for a particular user and validated another way for some other user of a different role or permission. Rather than writing a bunch of code in the controller though to do validation, I would encapsulate the validation in an object and do the correct validation based on the role. The same idea could be used based on the view or action that is called if an object were to be used for different views besides a login form. So I suppose, in the end, it would be best for code reuse to implement the validation outside the model.
A validation object outside of the controller is interesting. I believe it was touched on earlier in the comments as well. For this sort of thing, would you need one validation object per view?
Hey all, I have worked out a demo (complete with a Video!). I would love some feedback:
I've seen two main approaches to this problem.
1) Like OO programming, the application defines what the field is called. This is the lowest common denominator of sorts - the highest order term possible without excluding possible derivations. In this case, Username is Email, but Email is not necessarily Username, so you would create error messages like Username (just like you specify argument / return types as the most specific generic type it could be).
2) The application raises error codes (you could go the C approach of Error Code 107294, or a more modern approach of E_USERNAME_INVALID). In both cases the client application contains a table (Struct) which maps application errors to plain-text errors. Many languages allow you to specify both an error code and an error message. Then the application can override the specific error messages which it needs to, and when it's an unrecognized error, simply output the text from the model.
The first approach is more common because it is easier, but the second approach is much more powerful because it is also language-agnostic - if someone chooses, they can write a new client which supports many different spoken/written languages, even though your model has no specific knowledge of what languages it'll be ported to.