So the company I work for has quite an unorganized approach when it comes to our site. All of our scripts are procedural with cfincludes thrown within. I've been wanting to organize this into an internal API that the other web developers would use to do whatever (because making a change has me going thru and locating EVERY other instance that change needs to be updated).
I finally have a live example and showed the boss. It follows what I assumed is the normal method (from my googling). Service layer > Gateway & DAO > Beans, with some factories to help object creation. It works well and does exactly what I had wanted it to accomplish. He's impressed with it and agrees that we need to spruce up our code and better organize it but doesn't see the advantage of using this method of object oriented API calls to a large list of cfincludes to accomplish the same thing. In essence, from the way he explained the cfincludes, it would work the same way as a method call.
He's asked for the advantages of my approach vs this cfinclude and for the life of me I can't really find any obvious advantages other than grouping similar data all within one object. Is there anything else or rather perhaps it would be advantageous to go with the cfinclude approach?
Readability, maintenance, and adherence to proven object-oriented paradigms would be the most important aspects of building a ColdFusion application using a true service layer of CFCs / objects, rather than a multitude of cfincludes, which is amateurish at best, and can cause garbage collection nightmares at its worst.
Readability
Let's say you have a cfinclude called _queries.cfm, which include all the calls for your application. Then, at the top of your employee page, just before you output all the employees, you do this:
<cfinclude template="_queries.cfm" />
<cfoutput query="employeeQry">
Where did employeeQry come from? Is it one of the queries in that template? What does it do? Do I need to include that template when I only want just employees? What if it has all the queries in the site...do they all need to be included every single time?
Why not something a little more readable, like this:
<cfset employeeQry = request.model.queries.getEmployees() />
<cfoutput query="employeeQry">
Ahhh, there we go. At a glance, without knowing anything about the nuances of your system, I can identify right away:
Encapsulating business logic in a service layer (CFCs) increases the readability of your code, which is going to make the difference when you get into the next topic.
Maintenance
You get a hold of a new CF app that you're in charge of, and open up the employee page to find the <cfinclude template="_queries.cfm">
template above.
Inside that, the original developer leaves a comment saying something to the effect of: "Let's not run all the queries, let's just run a specific query based on a parameter", and then you see something like this:
<cfswitch case="#param#">
<cfcase value="employee">
<cfinclude template="_employeeQry.cfm">
</cfcase>
<cfcase value="employees">
<cfinclude template="_employeesQry.cfm">
</cfcase>
<cfcase value="employeesByDept">
<cfinclude template="_employeesByDept.cfm">
</cfcase>
</cfswitch>
...so you look at this and think, well...I need to modify the employeesByDept query, so you crack that template open and find:
<!--- employees by department --->
<cfif args.order_by is "ASC">
<cfinclude template="_employeeQryByDeptOnASCOrder.cfm">
<cfelse>
<cfinclude template="_employeeQryByDeptOnDESCOrder.cfm">
</cfif>
...and by this point, you want to shoot yourself in the face.
This is an exaggerated example, but is all too familiar in the ColdFusion world; a hobbyist mentality when architecting Enterprise-level applications. This "include within include within include" nightmare is something CF developers deal with more frequently than you might think.
The solution is simple!
A single CFC that encapsulates the business logic of producing queries for your Employees.
<cfcomponent>
<cffunction name="getEmployees" returntype="query">
<cfquery name="tmp">
select employeeID, name, age
from employees
</cfquery>
<cfreturn tmp />
</cffunction>
<cffunction name="getEmployeesByDept" returntype="query">
<cfargument name="deptID">
<cfargument name="order_by" required="false" default="ASC">
<cfquery name="tmp">
select employeeID, name, age
from employees e
inner join empToDept etd on (e.employeeID = etd.employeeID)
where etd.deptID = #arguments.deptID#
order by name #iif(arguments.order_by is 'asc',de('asc'),de('desc'))#
</cfquery>
<cfreturn tmp />
</cffunction>
</cfcomponent>
Now, you have a single point of reference for all the information you wish to produce when querying your employee database, and can parameterize/adjust it all at once, without having to dig through mountains of includes within includes within includes...which is cumbersome, and difficult to keep straight (even with adequate source control).
It elegantly allows you to write a single line:
<cfset empQry = request.model.queries.getEmployees() />
or
<cfset empQry = request.model.queries.getEmployeesByDept(14,'DESC') />
and makes your job maintaining the code that much easier.
Adherence to Proven Object-Oriented Paradigms
Your boss announces that a Java rockstar has joined the team. You're very eager and excited to sit down with him since you've primarily been stuck in CF for these last few years, and want an opportunity to show him some of your stuff, and possibly learn from him as well.
"So, how does the application get access to the data?" he asks you.
"Oh, we have a series of queries that we call on various pages, and based on the parameters, we'll pull different types of information."
"Nice", he says, "So...you have a service layer for data object model, that's great."
Not really, you think. It's just includes within includes...but he keeps going,
"That's excellent, because one of the new things we'll be adding is a Contractor object, which is basically a subset of Employee, he's going to have a few different bits of functionality, but overall will act very much like an Employee. We'll just go ahead and subclass Employee, and override some of those queries..."
...and now you are lost. Because there is no subclassing an include. There is no inheritance in an include. An include has no knowledge of a domain or a business object, or how it is supposed to interact with other objects.
A cfinclude is a convenience to reuse common elements, like a header or a footer. They aren't a mechanism to reflect the complexities of a business object.
When you design/construct/implement CFCs as objects that reflect the entities of your application, you're speaking a common langauge: OO. It means that it is not offers you the ability to design a system based upon a proven structure, it extends that language of "OO-ness" to programmers in other technologies. Java programmers, C++/C# programmers, etc...anyone that has reasonable knowledge of Object-Oriented development will automatically be speaking your language and be able to work with you and your system.
Take heed of this final note: Not every application needs to be object-oriented. If your boss wants you to whip up a quick XML dump of the employee table and slap it on the website--yeah, you can probably forego an entire oo model for that. But if you are building an application from the ground up, and it is going to feature employees, users, departments, queries, roles, rules, tickets...in short: entities in a domain, it will be time to put aside cfincludes as your primary tool for reusing code.
Oh, and PS: That little note I left at the top about garbage collection--is no joke. I've seen CF applications incorrectly built, so that the Application.cfc itself calls cfincludes, and after hooking CF up to a JVM that can monitor realtime creation/destruction of objects in the GC, I've seen memory look like an EKG monitor.
Not good.
Shawn's response definitely covers the main points. To summarise all that into the key points your boss will understand .. the CFC approach will save him a lot of money down the track in terms of maintenance, and will keep his developers a lot happier as their job will be so much easier, and retention rate of skilled/motivated developers will be a lot higher than if he stayed with spaghetti code as a standard.
I agree completely with Shawn's response... and if you want to take your code to an even higher level, use a framework! Then it will really save you and other developers a lot of time, since everybody will be adhering to its own standards.
My personal preference is Coldbox, but any of the popular MVC/OO frameworks will do -- Coldbox, Mach-II, Model-Glue, FW/1. I've heard good things about CFWheels too but haven't used it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With