OOPhoto - Unique Coupling Of A Controller To A View
I've been thinking a lot lately about the Controller's relationship to the View in an MVC application. In particular, I have been questioning the way I am handling error messages in OOPhoto, my latest attempt at learning object oriented programming in ColdFusion. Currently, validation and errors are handled in this way:
- Controller gets service to validate model.
- Controller takes returned validation object from service and translates it into a validation object that the View can understand (ex. takes {Category:required} from service and translates it into {category_id:required} for View).
- View takes the Controller-created validation object and translates it into user-friendly error messages (ex. takes {category_id:required} and translates it into "Please select a category").
I chose this path of execution for a very specific reason - the View does not have to know anything about the Model; it only has to know about the Controller. This way, both the Model and the View can change their implementations independently without the other knowing about it. This, to me, seems like the whole point of a Model-View-Controller (MVC) architecture.
But, here's where I've been stuck lately - why do we have to translate validation errors twice? If the View is already coupled to the Controller, why not just have the Controller take the errors returned from the service layer (Model) and translate those directly into user-friendly error messages.
The reason that I didn't do this originally was that I felt it would too tightly couple the View to the Controller. But why? Why do I care so much about the coupling between the View and the Controller. I guess, I have it in my head that theoretically, with this architecture, I can then easily swap out Views or something. But is that going to ever happen? Will I ever have a View that is not Controller-specific (and vice-versa).
My gut is yelling at me, No - Views and Controllers will always be uniquely coupled.
I think it's time to change my strategy. I am gonna get rid of the double translation of error messages and have my Controller translate the Model-based errors directly into user-friendly error messages that it will then hand off to the View. This way, the View is still coupled to the Controller, rather than the Model (a good thing), but we cut out the second half of error translation that didn't seem to be adding any value.
To close, let's just think about a worst case scenario. Let's stay that I did have to totally change my View for a particular page in such a way that all of the validation messages needed to be changed as well. With my current logic in place that would require me to change my entire View and the Controller's error-type translation. With my new strategy in place, it would require me to change my entire View and the Controller's error-message translation. No matter how you slice it, worst case scenario, I am changing both the View and the Controller; as such, we can see that having the double translation really doesn't save us any work in the long run.
It's time to simply the process. Plus, doing it this way allows us to keep a consistent strategy with AJAX-based calls in which I was already having the Controller create the user-friendly error messages. The way I see it, the more consistent we get, the more likely we are to be on the right track.
Reader Comments
Definitely on board with this one, Ben. In the common example that is often given "what if you want a version of your app with Flex UI instead of HTML?", the Flex UI will include a Flex controller (the ActionScript). This speaks directly to your point: the View and the Controller have to know quite a bit about each other, just as the Controller has to know something about the APIs provided by the Model. As with many of these questions, there could be a reason for the 2x error translation, but if you can simply and improve consistency, I'd say Go For It.
Yes, I agree too. Maybe at the exception that the same model can me coupled to different views. For example:
* If the validation worked, you want the "Confirmation message" view.
* If the validation did not work, you want the "Error message" view.
That is 2 views coupled to the same model.
As well, why not define generic error messages in the model? Instead of "The email address is not valid" use a reference, like "EMAIL_INVALID". That way you can easily translate your application when it becomes popular (it happened to me before, I was glad I coded it that way). The copy you display (The email is invalid / L'email n'est pas valide) is a function of view, it should not be defined in the model.
Maybe I'm still woozy from banging my head against a wall all morning, but the idea of having the controller itself contain code to translate the error messages seems strange to me.
Having the controller determine if there are any errors, and if so hand off the error data to another action/object for interpretation into a user-friendly format before the view is called, that makes more sense to me.
I've always understood the controller to be (as much as possible) a delegator and a resource manager: given a certain scenario, it knows what model and view objects need to be called upon to accomplish the task. If the controller is actually doing some of the data processing work, then some other object is slackin'. :)
@Jason,
Yeah, my point exactly - with a new Flex interface, it comes with it's own UI; we are not recycling the view-specific controllers.
@Jean,
I did not even think about internationalization, but that is how I am returning the errors. As in my example, the Model returns the struct:
{Category:required}
... This is Model-specific, but then the Controller translates to:
{category_id:required}
... which is View-specific.
@Brian,
I understand your reaction - it is the basis for my current design choices. But they are simply not sitting well with me. I feel like being able to blur the line between Controller and View will add simplicity with out any real trade-offs.
@Ben
I have to agree with this example. There has to be some coupling between the View and the Controller (in most cases).
@Brain/Ben
Rather than using a structure based error collection, couldn't you create a class (cfc) to hold Errors? This in turn could hold methods to translate the generic error messages into something more user friend (i.e. it could look the error up in a Dictionary). Granted this does mean that the View needs to understand something that came from the Model (if the Model returns the object) -OR- the Controller has to load the new error object with whatever is returned from the model so the View understands the API. Just a thought.
@Andrew: Actually, in my most recent projects, I've been using a Feedback object in my apps. My Feedback object is essentially a bean stored in the session scope that I can populate with an array of error messages to display to the user (or simply a message denoting a successful transaction).
One of the properties of the object is "displayOn," and it stores the name of the event/action where the feedback should be displayed to the user. So all the view page has to do is check to see if the value of session.Feedback.getDisplayOn() is equal to the name of the current event/action. If it is, the view page calls a function in the Feedback object that displays the message and then clears the data from the object. So all those view pages have the same 3 lines of code for displaying any relevant feedback messages to the user.
The use of a dictionary or some sort of look-up service might actually make more of a case for Controller-based translation, not sure.
The thing I can equate this somewhat to is the use of "Exit Fuse Actions" in FuseBox. Forgive me if I'm wrong, but in FuseBox the Controller sets the ExitFuseAction variables so that the View doesn't need to know how to display link actions.
To me, this seems very parallel to internationalized page-verbiage. Instead of outputting a title, the View could output:
#PageData.Title#
Or something.... Then the controller would be responsible for populating the key page text.
As far as the View understanding the API, I am OK with that. It has to do that to some degree anyway. After all, when it uses queries or objects that come back from the Model it still needs to know how to use those objects. So, there is going to be some coupling the API, obviously.
But, I think there is a difference between the View calling the Service layer directly and the View using Model-objects that are stored in the page data / event / renderer / what ever you call your event variables data bag.
@Brian,
From what it sounds like, with your DisplayOn() method, your View is not really creating any error messages - just displaying ones that have been defined elsewhere. I think this goes along with the idea of the View consuming user-friendly error messages rather than translating error-types into error-messages.
Perhaps what you are against was the fact that I was having the Controller do this translation rather than the Model?
@Ben: Your understanding of the use of exit fuseaction variables (xfa variables) in Fusebox is spot-on. It's another way of letting the Controller determine what goes where, since it controls where the hyperlinks and the form action(s) on a page point to.
And yes, my hangup was about the Controller doing the error translation.
Here's a question...I understand why you populated your Errors struct with flag-like words such as "InvalidValue" or "Required": it's much easier to write conditionals based on error values when the number of possible values is small. But why not add a human-friendly error response at the same time to the structure? You could make it such that you still use StructKeyExists to check for the existence of the keys, but then have the view display the human-friendly error response rather than the computer-friendly flag word.
That way you don't have to do any interpretations at the View or the Controller level: it's just a matter of calling the human-friendly data out of the Error data structure.
And when you think about it, most validation routines are concerned about validating data provided by the user rather than another module of code. Doesn't it make sense then that error messages are more likely meant to tell the user what to do next rather than to tell the program what to do next, that the user is really the primary audience for those messages? All the app usually needs to know is whether validation failed or not.
@Brian,
My concern there is that it ties the Model to the View in ways that seem odd. For example, let's say that I have an Event object that has a field, "DateStarted". But, on the input form, the field is labeled, "Event Date". If the Model produced the error, it might say something like,
"Please enter a valid date for Date Started."
... but, really, I would want a message to be more like:
"Please enter a valid event date."
The model only knows about its own naming conventions, not the naming conventions used by the interface architect. As such, by having the Model create the error messages, the Model then becomes tied to the View and going forward, all views that use that validation would need to use the same naming conventions.
@Ben: I see your point.
What you described reminded me of a post on Peter Bell's blog where he was wrestling with a similar problem: how to handle metadata about an object that was more view-specific. You and I (more you) actually both commented on that one:
http://www.pbell.com/index.cfm/2008/7/22/Generic-Admin-Screens--Who-knows-what#c4BDB215A-3048-749A-93B744C5A4EBCF0E
This is obviously an area where there's no solid answer to the problem. I think you're right that the View's access to the model should ideally stop at the Service level, so that's probably a better place to keep and/or process any data specifically targeted towards a particular view or views. I still don't think the Controller should be doing the interpreting beyond routing the raw error to some other Model object that does the translation.
@Brian,
I remember Peter's post. I'm still working on wrapping my head around everything Peter says. He has a much better understanding of OOP than I do plus a sense of the pros and cons of moving stuff around. Ultimately, I believe he would also say that adhering to anything from a "Strict" view point is potentially more of a trade-off than is necessary. As he puts it - his goal is not to write OOP applications - his goal is to write easy to generate, easy to maintain, and easy(ier) to de-skill applications.
Not really sure where I am going with this comment... other than to maybe say that the separation of View / Controller / Model is really continuum of pros and cons, of benefits and trade-offs.
That said, I am trying to create separation; don't think that I have given up on that. On my latest post:
www.bennadel.com/index.cfm?dax=blog:1334.view
I think about creation more implementation hiding between the Controller and the Model.
@Ben,
I'm not sure I agree that the model or the view should know about the naming conventions. I think the comment that Jean made was spot on. Whether you use resource bundles or whatever, providing a key so the concern over what the label or the error message are aren't even with the view.
having an rb with:
label_DateStarted=Event Date
invalid_DateStarted=Please enter a valid Event Date
your view still has to know what field to show, just not what label goes with it. it just shows rb.label_dateStarted or something like that
your model still has to know what field, but it just adds "invalid_dateStarted" to the cue of errors to report. It isn't outside of the realm of possiblities to do some pattern matching and replacement in those messages either.
where, in your view you were displaying the messages just loop over the cue showing rb[messageKey] or something along those lines
even if you don't have translations for different languages yet, you have killed 2 birds with one stone, removed the knowledge of the wordings from the model and the view and given people the ability to change labels/messages without modifying any actual code.
The point of MVC is that the model doesn't know about the view or the controller. It is absolutely legitimate for the view to know about the model. It's often stated that the job of the controller is to stand between the view and the model, but this is a bit misleading. The view should not directly *manipulate* the model - it should do this via the controller - but there's nothing wrong with querying the model directly from the view. After all, the view's job is to create a representation of the model, so trying to isolate the two is a bit futile.
@Jaime,
Ok, I'll buy that.
@Matt,
I am comfortable with the idea of a label/error lookup. That is not my main concern as those are still defined by people who are designing the interface. My concern was that the Model would return the user friendly message. I am ok with the Model returning some sort of key'd message index (ex. Required, InvalidLength).
An interesting debate, Ben, but I remain with a few unanswered questions.
* If, as has been stated on this page, the View has to know the Model, why then does your Controller transform data from the Model into something the View can understand? Doesn't that mean that the View has insufficient knowledge of the Model? You could eliminate that transformation instead of grouping transformations in the Controller.
* It seems as if you're just having a single Controller for your complete application. But what if that's not the case?
* As a developer in a multilingual country I am all too familiar with the problem of translations. We're handling translations as part of the Model, i.e. Views use separate components (CFC's) to translate Model data when and where appropriate. That way, even translations can be managed in a single place and reused in multiple apps. Does that make sense to you?
In my view (ha), the display of data/information from the Model to the user is the sole responsability of the View. The View decides whether an error status is displayed as a message or as an icon or whatever. The Controller(s) should not touch data from the Model, except by using other Model services to do so. That way, your Views can be used by multiple Controllers if required, and multiple Controllers can steer the same View if that is appropriate. OK, you'll need to stick to a few rules to make that happen, but at least it can be done... E.g. we always instantiate our main translation component in the APPLICATION scope, and our Views always use the (local) variable name AT (Application Translations) which points to that component to access those translations. As long as the required keys are available in the translation component, all our views are capable of being used in multiple apps...
@Wouter,
I am sorry if I am not explaining myself very clearly. Here is the biggest point that people seem to be glossing over, in generic format:
FORM.item_id
Object.ID
Let me explain. As an interface developer, maybe I pass an ID via the form. However, maybe I have a page that edits more than one thing and as such, need to label things differently, prefixed in some way. So, depending on the interface, the same value might be:
FORM.id
FORM.item_id
FORM.item_id_list (as part of a list)
FORM.id_#intI# (part of a loop)
All of these values correspond to Object.ID in some way. Therefore, depending on the type of View, the Controller has to take a particular set of ID-values and translate it to Object.ID.
So that's one way direction of translation (View to Model). But, now, let's go the other way (Model to View). Let's say we have a contact form with the following information:
First Name / Last Name
... then we go and build our application. Now, let's say we decide to change it to be:
First Name / Family Name
If our Model was in charge of defining the user-friendly error message, it might have:
"Please enter your last name".
However, this would NOT be aligned with the label of the field in the View. Therefore, there has to be something that translates the erroring property (LastName) into the error message "Please enter Family Name" which is View specific, not Model specific.
Does that make more sense?
Your use of the word "translation" sent me partly on the wrong trail; but I have seen too many components and libraries without a clue as to what "multilingual" means, and I tend to react rather strongly to that... Hope that explains, if not excuses, things ;-)
Back to the subject. Yes, of course your controllers have to "interpret" (or "massage") the data from the views before passing things on to the Model components - that is the essence of their being. I'm 100% with you there.
But: should a Model be in charge of user-friendly messages? Not in my view. A Model has to say that something is wrong (or right); how the message is presented to the user is the responsability of the Views. The Model shouldn't care whether you want to display "family name" or "last name" - if your Model is being reused in several apps, then perhaps at one time it's the former, and in another app it may be the latter. To me, saying "family name" or "last name" is a form of "translation" that can be handled with a mechanism like the one I described or something similar - in the View of the application, and certainly *not* in the Model.
When I develop applications, I try to make my Model components as reusable as possible, because they are the part of an application that is most likely to stay the same... and if it changes, chances are I'll want it to change everywhere it is used. Views (and the texts they display) are more volatile, since they may have to be adapted to specific groups of users. So even when the Model behind them remains the same, the View of a Person in two apps may be quite different and even use different labels and message text for identical Model properties...
Of course, my point of view is strongly influenced by the fact that I build rather small apps within the context of a large company, so your priorities may require other solutions.
@Wouter,
No worries on the "translation" misscommunication. And to be honest, I know very little about multilingual sites. But, as far as the rest, I think we're pretty much on the same page.