OOPhoto - Data Validation Layers And Night Club Bouncers
The latest OOPhoto application can be experienced here.
The latest OOPhoto code can be seen here.
I've made a lot of progress in OOPhoto, my latest attempt at learning object oriented programming in ColdFusion, but I have a lot more to go. I think I have just barely begun to scratch the surface of object oriented programming and what it all means. Based on some different conversations that I've had, I believe that I have missed some of the most key concepts in what makes OO so great.
Last Thursday, I had dinner with Dan Wilson. Now, I don't want you to think that when two ColdFusion coders get together, all they can talk about is coding, but I'd be lying to say that we didn't talk a good amount about object orientation. While Dan appreciates all the effort I have put into this project, he is concerned that I am still treating objects too much like simple "Data Containers;" I am manually setting and getting data when I should just be getting things "processed." After all, a big part of object oriented programming is about "asking" objects to do things for you.
Unfortunately, we didn't have a pen and paper, so it took me quite a while to follow what he was saying. After the first few verbal explanations, I kept questioning what exactly was different between what he was saying and what I was doing. To me, our two approaches sounded very much the same. Ultimately, I think it all boils down to something I will refer to as the "Rule of Five." Don't try to look that up - I'm making it up right now. The Rule of Five states that if you Controller action is more than 5 lines of code, you're probably putting logic in the wrong layer of your application.
When you approach the code with the Rule of Five constraint in mind, the idea of processing data becomes quite different. You don't have a free line of code in your controller to create beans and move data into them - that's simply too much work. To keep with the 5 line limit, you can't mess with your form, you have to ask your form to be processed. In doing this, no only are we starting to get into the mind set of asking objects to do things for us, we are starting to think in terms of "Actions" rather than "Data." In stead of moving FristName, LastName, and Email address into a Contact object, we are simply asking our service layer to process the contact data form with a ProcessNewUser() action.
After moving more in that direction, what we end up with an extremely light weight Controller and a very rich Domain Model. When you look at the MVC/OOP dogma, this lines up nicely with its teachings. Your controller shouldn't really do anything - it should just take requests and hand them off to the Model. The controller doesn't really do any "processing."
That sounds good, but I just don't buy it yet. At least not hook, line, and sinker.
During my conversation with Dan, the idea of a night club bouncer kept coming up. To be honest, I don't fully remember the context in which it was brought up, or what exactly it was meant to symbolize, but I think it had to do with input points to an application. You go, you show your ID to the bouncer. He then process it, and decide whether or not to let you in.
I felt bad that I couldn't remember what Dan was talking about, so I kept going over it in my mind this weekend. That's when something dawned on me. Think about this situation as if it were a Form on a web site. Here, we have a user that needs to enter their ID in order to gain access. They then submit the ID to the bouncer, who processes it and returns True or False as to their ability to enter the club. Seems pretty straightforward, right? But think about what data is being passed around. If you take into account the Rule of Five described above, ideally, you would just hand over your wallet (FORM scope) to the bouncer and ask him to process it. He would then, in turn, open your wallet, rifle through it, probably get off a little on seeing pictures of your wife (the sleazy bastard), grab what he believes is your ID, scans it, and then lets you know if you can enter.
When you read it that way, I hope you get a strong guttural feeling that this is completely insane. You never hand your wallet over to someone and let them go through it. That's just bananas. What you do, in reality, it open your own wallet, select just the information that is needed, and hand that over. This way, the bouncer doesn't have to know about your wallet and you don't have to worry about him seeing half naked pictures of your wife. Your wallet (View) and the bouncer (Model) have, in object oriented terms, high cohesion and extremely low coupling.
If you follow my logic so far, the question then becomes, who opens the wallet, pulls out the ID, and hands it over to the bouncer? Obviously, it has to be something that liaises between your wallet and the bouncer. Is this the Controller? In my current OOPhoto application, the answer is Yes. The Controller is responsible for taking data out of the form, packaging it in an object, and handing it off to the service layer.
But it doesn't have to be. From what I discussed with Dan, it seems like one strategy would be to create a utility object that is designed to handle the form processing of a specific form. The controller could then instantiate one of these objects and then hand the FORM scope off to it, which would then take care of the processing. This would allow your primary Model the ability to process just the information needed without having to anything about the way in which the View is constructed; the hand off of data would be handled by the form processing utility object.
My biggest fear here is that all we are doing is adding another object and moving my current Controller logic into it. This feels like we are adding more complexity without any foreseeable benefit. Certainly, we don't really encapsulate changes; if the View changes, now I need to change the View and the utility object rather then the View and the Controller. Still two changes. Still a one-to-two ratio (one change, two affected places).
Uggg, I have some more thinking to do on this topic, but unfortunately, I need to get to work. To be continued.
Reader Comments
@Ben,
I would challenge your Wallet = View assumption. In your application, the decision about what data to share is actually handled by the User, and the same with your bouncer scenario: YOU decided to only hand over your ID rather than the whole wallet. The bouncer serves a dual role (we humans are so complex!):
* You are the User: you provide the input
* The bouncer demands "ID, please!" and holds out his hand, that's the View: it lets the User know what is expected and what will be accepted
* The bouncer is also the Controller: he expects an ID, not an entire wallet, and he passes the ID off (in his head) to the Model
* Management sets rules which function as the Model (18+, 21+, on the VIP list, etc): the Controller passes along the ID to the set of rules defined by management and gets a "go" / "no-go" back
* Based on the results of processing the ID vs the Model, the bouncer/Controller then returns the proper hand/View to the User: the hand goes up in your face = "FAIL!!" or the hand waves you in the door = "PARRTEEEE!"
The example is muddy because the bouncer/Controller also implements the business rules and the hand/View, and that's not good MVC, but humans can just process a lot more decision points than any ol' server can and we seamlessly integrate our I/O at the same time.
Not sure if that helps ... but I hope so.
-jfish
@Jason,
You raise some great points. While some of my logic was flawed, what I was really trying to do was point out that we can't just pass Form data directly to the Model because the Form data can be in any format.
For example, let's say he has a hand-held device that requires a social security number (just go along with it). You hand him your ID (View). Then he (Controller) takes the ID (Form data), reads the SSN out of it, and enters it into his hand held device (Model), which checks against a database and returns some sort of response.
Maybe that's a better illustration of how the Controller needs to take the FORM data and package it - not just mindlessly hand it off to the model.
Hi Ben,
I've been trying to learn OO principles as well and have used a couple of the popular frameworks to date. Nice post!
I like the notion of the 5 line rule.... the procedural bones in my body go haywire VS such a rule!
;-)
@Yves,
Glad you enjoy the rule... but please keep in mind that I am extremely new to this. Please take all of my thoughts with a grain of salt.
@Ben,
Hmmmm. I like that illustration. I think, though, that the Controller in that situation does just send on the scan data mindlessly, doesn't it. I mean, the scanner literally just grabs data off the license bar, maybe ID + date of birth + name, whatever's on there, and it sends it to the model. The complexity of decision-making then, is completely on the model: maybe 1) check that date of birth = 21+ years old, else fail; if success, then 2) check that name is on the VIP list, else return "let him in"; if success, then "let him backstage". Now, if you have a Service separate from a DAO, the Service will know to parse out just the DoB for step 1 and just the name for step 2, and the DAO (or Gateway or whatever) simply processes the data request. Even the top of the model may never use the actual ID value, although it was sent. In either case, the Controller simply gets back a 'response'.
For example, in your PhotoGalleryService object, it knows to expect a string value when asked to GetGalleriesByKeyword(); even if you sent it 5 other args in addition, it's only going to use the Keyword value to process the request.
Just thinking out loud here ...
@Jason,
I don't think it scans the ID mindlessly. I'm not talking a laser or barcode scanner here. I am saying the bouncer has to take the ID (Form data), look at it, find the SSN number, then manually enter than number into a hand held device for processing.
Obviously, this is not what really happens at bars, but I'm just trying to make the point that the "submitted" data, cannot just be "processed" without being manipulated. After all, in this scenario, there would be no way to have the ID go right into the hand held device.
@Ben,
Aha, I see what you're talking about now. Still, though, in an application, doesn't the User do the entering? I still think the hand-held device is the Controller, regardless of who enters, I guess.
It's always this theoretical stuff that seems so much more complicated than the actual coding! LOL
@Jason,
I guess, ultimately, where I'm going with is that I want to try to avoid things like this (In Controller):
SomeObject.Process( FORM )
Granted, I am very new to all this OO stuff. And granted, so much of it has to do with trade offs; but, I just strongly dislike the idea of passing my FORM scope to anything. I feel that there needs to be an intermediary process that extracts data form the FORM and presents it to the Model. Currently, I let my Controller do that. Maybe it needs to be another object that sits between the Controller and the Model. I am not sure.
All I know is that by passing the FORM directly to another object, it means that interface coders have to know about the structure of my Model, and likewise, my Model would need to know about the structure and naming conventions of my FORM.
This feels like high coupling to me.
Now, don't get me wrong. I do LOVE the idea of having naming conventions and standards. What I don't like is forcing a part of my system to have to rely on those standards in order to function.
With respect to the form issue and naming conventions, it seems to me that ColdFusion just can't do the OO things that would make this "jive".
The current strategy, as you know, is just to pass the form along and require that form ids = object properties = database columns. If you don't, you need to fatten your controller.
It seems that traditional OO languages would handle this differently, but they are not centric to "data processing". You are going to spend a whole lot of time and seemingly an extra layer if you try to sway from this current standard.
For instance, in every "learn OO" thing, the examples are applicable to real word things. Even your bouncer situation is a real world processing thing. I've never seen a good example of "pure" OO when it is specific to what we do, process, store, retreive, validate and display data. I've seen examples that are systems for doing the above, (i.e. coldfusion itself).
So, for us, it gets modified and that's ok. We bend the rules a little here and there because it just won't ever make sense without adding an extreme amount of complexity.
@Justin,
I hear you. Right now, I am leaning more towards the fattening of the controller because I simply don't feel good about relying on naming conventions to pass data around. Something about that just seems wrong and against everything that MVC seems to want to be about.
@Ben,
I would agree. I think the Controller isn't necessarily 'fat' simply because it knows to handle the translation of form['var1'] => method(arg1). If var1 is called 'firstName' in the form and 'userFirstName' in the first cfargument of the method, I don't see a problem with that. That's what it's controlling after all, isn't it? the flow of data between View and Model? Using separate Service and DAO layers as you are further encapsulates both the View and Controller from the database as well, further reducing the need for things to match up and down the line, IMO.
Clearly there are cross-over points that feel less than separate: such as having Validate() on the bean which the services are passing around and having very similar validation in the View, such as JavaScript, if you want enhanced user experience. But I think that illustrates the point that each piece of the puzzle certainly does have to know something of the overall app. It's never simply a matter of magically stitching together collections of entirely generic view, controller, and business objects and getting a recognizable result that meets a specific user need. Each element is part of the larger application, and as much as each area of concern needs to be stand-alone in the sense of handling all its own actions, each piece does have to 'know' its own role as well as how to interact with the other pieces it touches.
Just my 2c
@Jason,
Yes, I think all the pieces do need to know about each other to some degree. However, I think that degree is sometimes too much. For instance, sometimes I see people try to have their client-side validation be generated by the Model. To me, this is completely against the idea of MVC. Really, the view is an entirely different experience. If a "front end developer" wants to add client-side validation, then so be it. If he/she does not, then so be it. If they want to put a CAPTCHA item in. So be it. If they want to have people confirm their password or email address twice to cut down on errors, so be it.
These are all decisions to be made by the front-end developer.
In many of the cases the front-end developer and the back-end developer is the same person. I know it is in my case. As such, I think people want to blend these layers a bit to make life easier.... and I think this is fine, if you are comfortable with it. But, I just want it to be clear these decisions are "bending" the rules that are set forth by MVC and OOP (at least that's how it seems to me - heck, I'm just trying to wrap my head around all of this stuff).
And, just to stress, I am not saying that any of these decisions is wrong! In fact, I am inclined to believe that anything that makes web development easier is the "right" way :)
Personally, I am just not ready to make some of these leaps yet. I don't feel I understand the principles to the point where I can make educated decisions on which rules to bend and which rules to break. As such, I want to keep it as clean as possible until I have some basic understanding. At that point, I can go back and see where processes can be optimized by blurring the lines of responsibility.
@Ben,
Could not agree more. Just because much of the same validation is likely to happen in both the Model and the View does not mean that the Model should somehow push all the way up to the View. First of all, only the Controller should be pushing anything to either of them, and secondly, as you point out, the UI is responsible for handling the user experience, whatever that may need to be. We're on the same page that simply because it seems easier to put similar validation in one place ("View needs a username" and "Auth Bean needs a username") doesn't make it OO, and in fact that would rely on the Model speaking to the View, which definitely 'breaks' (whatever that means) MVC.
My point on the validation wasn't that the Model should push all that, it was simply that in your case you are going to have some apparent "code twice" situations, simply because you're doing both the Model and the View and they will seem to share some validation schemes. I entirely agree with you that they need to remain separate, and I think the result of that separation will naturally make the Controller a bit more than 5 lines simply because the Controller does have to know enough to translate between the View and Model, which is its job.
Still loving this OO journey, and learning to put some much finer and clearer points on my own OO design assumptions in the process. Thanks!
@Jason,
It's a fun journey. Thank you very much for being a part of the conversation.
Are you sure that we aren't forced to bend the rules because of the nature of our applications. Afterall, we rely on a set of W3C standards, Browsers, and HTTP that has nothing to do with OO. ColdFusion doesn't even provide all the tools to produce "real" OO. Heck it's because of the browser that we are forced to run two separate, but similar validation.
For instance in the controller:
myBean = beanService.createBean(form);
myValidator = new Validator();
errorStruct = myValidator.validate(myBean.getValidationStructure);
//do stuff with errorstruct that displays on view
Validator has the ability to break down a custom structure ruleset. The "ruleset" structure comes from the bean that assemble this structure for the validator when the getValidationStructure() is called. The controller manages the errors from the validator.
The "ruleset" structure created by the bean for a validator object might be psuedocode:
//array of structures
[
{property="property1",
validType={minLen=5, maxLen=50, required=true},
customValidMessages={"Property 1 is too short",
"property1DisplayName is too long", "Property 1 is required"},
{property="property2",
propertyDisplayName="Property 2",
validType={required=true
//by default, no custom error messages are required
}
]
This is what jquery validate does on the client side.
However, this still requires that your view know about the model naming conventions.
@Justin,
There is nothing that even requires us to have client-side validation. We could build applications that only have server-side validation and they would work fine, just perhaps not as "snappy".
That being said, there does have to be a "contract" between the View and the Controller as to what the naming conventions will be. Otherwise, there would be no way for the View to interpret the errors that come back. However, there is nothing to say that what the Model returns has to be what the Controller returns.
Let's say the Model returns the error:
FirstName.Required
... signal that the FirstName property was required by not entered. There is nothing to say that the Controller cannot see that an decide to send:
"first_name"
... back to the View. As long as the View and the Controller agree on what the errors will be and what they will mean, then it is really irrelevant how the Model does its validation.
@Justin,
Well, yes, the Model would need to use the same var names as the View, if we followed this convention in our Controller:
myBean = beanService.createBean(form);
But my preference is for the Controller to handle the communication:
myBean = beanService.createBean(
username=form["myUsernameField"],
password=form["myPasswordField"],
savePreferences=form["savePreferences"]
);
At that point, some vars may match (savePreferences) and others may not, but the View and Model never have to know the details of the other. My Controller for CF may handle this differently than my ActionScript Controller in Flex, even against the same Model; basically, I want to the Model to simply report what it knows, and the Controller to translate that into what the View will understand. The View may in turn apply the Controller messages in a completely different way than originally expected, but it knows what it's getting and that's all that matters. I don't want the Controller to tell my View that it has to report errors in JavaScript alert(), because the UI designer may decide to show/hide a nicely skinned hidden div instead; I simply want to tell the View, "hey, we have an error with mistmatched passwords" or something, and the View takes it from there.
At least that's how I see the separation of MVC working, whether it's CF or any other language.