A good friend of mine IM'd me this morning, hot in the middle of a sales pitch to his boss as to why it was a good idea to get away from the Spaghetti paradigm and move towards some semblance of OO methodologies. Our conversation was short and sweet, but I thought I'd toss it out here in case anybody else has anything to add (or correct), for the benefit of any other developer who finds themselves in a similar "sales" position.
The Conversation
MyBuddyJack: My boss wants to know, why do we have to create instances of objects and go that route? Why can't we just use cfinvoke to get the data we need and just code everything an object needs right in the same cfc.
MyBuddyJack: I think he is wanting to code things procedurally, but with cfcs...that's the feeling I get. But what is the advantage to coding it more OO by creating instances etc.?
MyBuddyJack: I just told him it is easier to maintain and change down the road.
Doug Boude: persistence
MyBuddyJack: I couldn't really sell it like I wanted.
Doug Boude: for example, i have a user object. If I instantiate it, then I can keep it alive and interact with it, changing things about it here and there and maintaining those changes throughout the life of the object
Doug Boude: if I just use invoke, I'm treating it as nothing more than a dumb collection of functions
MyBuddyJack: he wants to put everything into the session scope
MyBuddyJack: so everything is always available anywhere
Doug Boude: and what would BE in session? Oodles of variables sitting there beside one another, all in the same bucket, differentiated only by some naming convention, if that?
Doug Boude: using a CFC as an object, you collect related things together, so you always know where to go for a specific thing
Doug Boude: if you didn't use a User object, I'm POSITIVE he'd probably create a User structure that lived in session to hold all of the user's info
Doug Boude: but, the methods he needed to act upon and with that user info would all be living here and there and everywhere...little semblance to anything truly organized and encapsulated.
Doug Boude: to move that user functionality, then, at some later date would be a nightmare because SOME Of the user stuff would live in application.cfm, some of it would live in some UDFs, some would live here, some would live there...no encapsulation
Doug Boude: it would be what we have always been used to: spaghetti
MyBuddyJack: Yeah that's a good point. I told him that if he has an instance of a User Object, then when he wants to output stuff he just calls the object, whereas otherwise he'd have to invoke everytime to get the data he wanted.
Doug Boude: but stuff it all into a user CFC, and voila! it's always packaged up, ready to drop into anything
Doug Boude: invoke is expensive...it instantiates the object first, then calls the method, then destroys the object again
Doug Boude: extra overhead
Doug Boude: why not just breathe life into it ONE time initially and have it waiting to be called upon instead?
Doug Boude: i could only see using cfinvoke under two circumstances
Doug Boude: one, when my CFC is truly just a collection of random methods that I will need on occasion only
Doug Boude: or two, when I need to call dynamically named methods, which isn't possible on an already instantiated object
MyBuddyJack: I think I understand encapsulating everything as a benefit. But is that it? What other beneifts are there to instantiating objects OR why is that better than procedural?
Doug Boude: you can instantiate objects and still code procedurally
Doug Boude: it's really two different things
Doug Boude: using CreateObject does not an OO application make
Doug Boude: like i said, there's the savings of overhead; there's the logical arrangement and grouping of both functions, business logic, and data
Doug Boude: there's the re-usability factor, should that ever be a desire
Doug Boude: if you use objects as the main basis for your app, then one day when the light DOES go off for them and they see the beauty in MVC, moving to a new framework won't be nearly as large a pain
Doug Boude: it's MUCH easier to make modifications to an app when you have things arranged in objects, and without having that old common spaghetti coder's fear, "What else is going to break if i change this?"
Doug Boude: but putting things into CFCs and then using CFINVOKE to interact with them is like starting your car long enough to get you to the first stop sign, then turning it off again. then starting it up again when you're ready to proceed, then turning it off again at the next red light. etc.
Doug Boude: in that analogy, it almost looks...ridiculous to do it that way
Doug Boude: and, it's easy to see the extra overhead involved with that analogy...extra wear and tear on your starter, extra time needed to actually respond to the light turning green, etc. Same thing in an app
Okay, actually there's no way to COMPLETELY distill this topic down to the point where you don't have to have a decent understanding of web development to grasp it. BUT, I do believe that I've been able to demystify the subject sufficient to allow even the beginner OOP/Model-Glue developer to "get it", which is what I wish I would have had a year ago when I left the comforts of procedural and embarked on my OO journey. That being said, this post is not one of the shorter ones I've done; necessarily so, as there's a lot of ground that I felt couldn't afford to be skimmed over. By taking the time to read through it, however, I do believe that a LOT of the "gray areas" that we OO newbies face, architecturally speaking, will be cleared up enough to at least give us a clue which direction to go. That being said, what follows is my PERSONAL understanding (which has worked well for me) of the Model-Glue Event Lifecycle. Enjoy.
UNDERSTANDING THE MODEL-GLUE EVENT LIFECYCLE
The Model-Glue framework can present an intimidating learning curve when coming directly out of the procedural world to that of Object Oriented Programming. One of the things that will help the Model-Glue student immensely is to have a solid understanding of how the Model-Glue event lifecycle works: being able to visualize the invisible. With this knowledge in pocket, many of the architectural questions that will inevitably arise will be more easily addressed. So, let’s explore what we mean by the term ‘lifecycle’, what the definition of ‘event’ is in a Model-Glue context, and what Event’s role is in a Model-Glue application!
A Lifecycle You Already Understand
Let’s look at a lifecycle you’re already familiar with: http request. Pictures are worth a thousand words, so allow me to illustrate this:
Looks familiar, doesn’t it? The request begins life when the client initiates it with an http call to our web server for a specific template. The server receives the request, grabs the template requested, and hands it over to the Coldfusion server for processing. The Coldfusion server renders the CFM template into pure HTML (since that’s all a browser understands), hands the HTML back to the web server, and the web server returns the HTML to the client. End of the request’s lifecycle!
When a client makes a request of a Model-Glue application, a similar process takes place. The focus of this post will begin (and end) at the point where index.cfm is requested and passed a parameter called “event” (eg; http://www.myMGapp.com/index.cfm?event=home). The receipt of this parameter is the first breath our Model-Glue application draws, and begins the internal process which we will now follow through to its conclusion.
What Exactly IS A Model-Glue Event?
‘Event’ is two distinct things:
‘Event’ is a simple value: a name, such as ‘home’, or ‘do.login’ (yes, compound event names are acceptable and even advantageous for organization’s sake!);
‘Event’ is a bucket.
“A bucket?”, you may be asking yourself. Yes, a bucket! Buckets carry things around, and at a certain point in our event lifecycle study we will see how the event changes from being simply a name to an actual value-carrying OBJECT that gets passed around within the application! It’s a beautiful metamorphosis, as you will see.
The Event Lifecycle in a Nutshell
Let’s begin with an overview of what takes place in the lifecycle of a Model-Glue event (We’ll be exploring each step in detail later):
A request is made, notifying model-glue to execute a specific event (eg; http://www.myMGapp.com/index.cfm?event=do.login). At this point, Model-Glue generates an empty Event Object (aka: bucket).
Model-Glue looks in its modelglue.xml file to find out what messages to broadcast to listening controllers ( <broadcast><message name="login" /></broadcast> ) for the 'do.login' event.
Controllers listening for the "login" message execute their corresponding functions ( <controller name="AuthenticationController" type="controller.AuthenticationController"><message-listener message="login" function="AuthenticateUser" /></controller> ).
Model-Glue executes any relevant result actions (behaves as an 'if' statement), if present.
Model-Glue renders any views that are defined for this event. HTML is delivered and the event lifecycle is over.
Event Lifecycle, Step One: Create the Event Object (aka: bucket)
The whole event lifecycle depends upon values being passed around within the application, so the first thing Model-Glue does after being notified which event to execute is to create an Event Object. Just visualize it as a bucket at this point.
After the Event object is created, any incoming form fields and URL parameters are immediately stuffed into it for safekeeping.
Event Lifecycle, Step Two: Reading the Event Definition
Now that Model-Glue has the Event Object all populated with incoming values, it’s time to discover exactly what should be done with them for the event name that was called. The instructions, or event definitions, that Model-Glue refers to when an event name is received all live in an XML document called “Modelglue.xml”. Here is what an event definition looks like:
The <event-handler> tag has three children: <broadcasts>, <views>, and <results>, and they are evaluated in that order. Let’s look at each one more closely, following our Event Object through the process.
Event Lifecycle, Step Three: Giving Shout-Outs (aka: making broadcasts)
The first thing Model-Glue does for a given event is to make any defined broadcast. A broadcast is simply a shout out that triggers certain CFCs to execute specific methods. In our example, the broadcast “login” is made. Model-Glue looks in another section of its Modelglue.XML file to find out what CFCs are “listening” for that particular message, then executes the relevant method. In this scenario, this is what Model-Glue found in it’s XML file:
Ah, so Model-Glue is going to invoke the “Login” method of the AuthenticationController.cfc (which lives in the “controller” directory). Here is an important thing to know at this point: for every method that Model-Glue calls, the Event Object that was initially created and populated will be passed in as an argument, like so:
Why? So that our methods can read values out of it (such as the username and password that were passed in via our login form) AND so our methods can put values INTO the bucket as well! In our example, the Login method is going to attempt to authenticate a user. If it succeeds, it’s going to add a ‘Success’ result to the Event bucket; if authentication fails, it’s going to add a ‘Failure’ result.
Summary of Step 3
Okay, the “Login” method finished, Model-Glue has the Event Object in hand, and there are no more broadcasts defined for this event, so let’s go on to the next step in the Lifecycle.
Event Lifecycle, Step Four: Decide What Results to Execute
Take a look at the <results> section of the event definition. We see two named results defined (so called because each of them has been given a specific name): success and failure. Recall how our Login method added one of two possible results to the event bucket, either “Success” or “Failure”. At this point, Model-Glue looks for any results in the Event bucket and executes whichever one is appropriate. Executing a result really just means “hey Model-Glue, you found a result named “Success”, eh? Then I want you to jump to the defined event named “home” and execute that now.” If a result named “Failure” had been found, then the “login” event would have been called, presenting the user with the login form again. When Model-Glue executes a named result and jumps to another event, the programmer has two choices at this juncture. To start the new event fresh with an empty Event object, or to carry the existing, populated Object over to the next event. This is controlled with the <result> attribute “redirect”. If set to ‘True’, a new, empty Event object is created; if set to “False’, the original Event Object is passed along.
Summary of Step 4
At this point, all of our messages have been broadcast and relevant methods executed; if we needed to redirect to another event, that was also already accomplished. We’ve now arrived at the final step in the event lifecycle: Render our HTML and send it back!
Event Lifecycle, Step Five: Render The Views!
(aka: create the HTML that will be passed back)
Since our “do.login” event was all about actions and not views, let’s look at the “home” event that a successful login attempt redirects us to:
This particular event doesn’t ask Model-Glue to make any broadcasts nor does it ask Model-Glue to look for and process any results. What it DOES do, however, is ask Model-Glue to render two different CFM templates, dspMainContent.cfm and dspLayout.cfm. There are several important things to note here.
First, that we can tell Model-Glue to render any number of individual CFM templates. Second, notice how each template is given a “name” attribute…the rendered HTML will later be referred to by this name. Third, the order we list them in is irrelevant with one WHOPPING exception: the last template rendered is the ONLY one that Model-Glue will return to the browser. So what happens to the other Views that were rendered and placed onto the Viewstack (notice how I just increased your Model-Glue vocabulary ;) )? All other rendered templates are “included” in the final template, in nearly EXACTLY the same manner as using a CFINCLUDE, only slightly different. See a previous post I did on Views called “Model-Glue Views Demystified” for more detailed information on this topic. Now, let’s find out where our Event Object/Bucket is relative to the views that are being rendered, shall we?
Model-Glue makes the contents of the Event Object available to your CFM view templates in an object referred to as “ViewState”. It, too, is an object, very similar to the Event Object, and it contains all the values that your template could possibly need. You may be asking yourself, “why do we need the ViewState Object if we already have a perfectly good Event Object?”. Valid question, and the answer lies within the philosophy of object oriented programming and the two cardinal virtues that a framework like Model-Glue seeks to uphold: Encapsulation and Autonomy. Suffice it to say that in order not to violate basic MVC principles, copying the contents of the Event Object into the ViewState Object was the better thing to do. Okay, take a look at the following two lines of code taken from within a view template:
Any CFM template created for use within a Model-Glue application can absolutely count on the presence of “ViewState”. The value “firstname” that we’re retrieving from ViewState is a value that was placed in the Event Bucket at some point previously in the event lifecycle, likely by one of our broadcasts. Are you seeing the beauty of the Event Object yet? From the beginning of our event call, that bucket was passed around here and there, values being put into it, redirections being made as needed, until finally we arrive at our views where all of the previously saved values are made available for use within your templates. Strings, Arrays, Structures, Queries, and even other Objects can be stuffed into the Event Bucket and made available via ViewState. No limitations in that arena!
Slightly off topic from this article, let’s quench some curiosity and sneak a quick peek at how the final view being rendered includes content previously rendered in the viewstack, in this case, how “body” is output within “final”. Take a gander at the following few lines of code:
<cfset body = ViewCollection.getView("body") />
<div class="contentbody">
<cfoutput>#body#</cfoutput>
</div>
Yes, it’s really that simple. All rendered views are placed onto the viewstack, referenced by the name we gave them in the event definition, and are available within our template by accessing the “ViewCollection”. Again, for more detail on Model-Glue views, see the “Model-Glue Views Demystified” post.
Views all rendered and the final HTML complete, Model-Glue returns it to the web server, which in turn returns it to the browser that requested this particular event in the first place.
Summary of Step 5, and the end of our Event Lifecycle
This is the end of our Event’s life; it is disposed of properly. RIP.
One Final Bit of Trivia
If you’re curious about what the Event Object really looks like, here’s a CFDUMP showing it and its methods:
CFDUMP of the Event Object
Conclusion
As its name implies, Model-Glue is a framework that allows a developer to create code that is truly self-contained and reusable by acting as the glue that binds all the pieces together. It accomplishes this by managing the one common denominator that the Model, View, and Controller all have in common: The Event Object. From the client’s initial call to the final delivery of the requested HTML to the browser, it is the Event Object that the developer must understand and interact with in order to create a working application. Only a mid-level understanding of the event lifecycle is required to meet the majority of architectural needs. How to incorporate security, how to ensure the display of appropriate navigation, how to easily add in new functionality: these are all common challenges that understanding the Model-Glue event lifecycle will enable any developer to overcome.