Categories
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

<< May, 2013 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
262728293031
Search Blog

Recent Comments
Re: Disappearing IE Popup Window During Save/Open Dialog (by LZ at 4/20 7:58 AM)
Re: Create Dynamic WHERE Clauses in PHP (by pooja at 3/20 7:29 AM)
Re: Just What IS a 'Service Layer', Anyway? (by EugenK at 3/07 7:56 PM)
Re: Using Google as your CF Mail Server (by 5starwebteam.com at 2/25 1:27 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Steven Benjamin at 1/25 11:43 AM)
Re: What is an 'Advanced' Coldfusion Developer? (by ColdFusion Developer at 12/24 5:14 AM)
Re: Equivalent of SQL "TOP X" in Oracle (by Ashenafi Desalegn at 12/06 5:29 AM)
Re: PHP Export to Excel Snippet (by serene at 12/05 1:44 AM)
Re: Just What Is 'Application Logic', Anyway? (by Arif at 11/13 8:06 AM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Aaron at 10/22 2:31 PM)
Re: PHP Export to Excel Snippet (by Jafar Shah at 10/10 4:28 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Chenelle S at 10/04 12:53 PM)
Re: PHP Export to Excel Snippet (by Kilo at 9/26 5:20 PM)
Re: Porting Coldfusion Code to Mura (by tariq at 9/03 9:51 AM)
Re: Just What IS a 'Service Layer', Anyway? (by James at 8/27 4:06 PM)
Re: Calculating Business Hours (by helen at 8/14 2:54 AM)
Re: What IS 'Business Logic', Anyway? (by dougboude at 8/06 11:30 AM)
Re: What IS 'Business Logic', Anyway? (by Adrianne at 8/06 10:29 AM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by dougboude at 8/04 4:39 PM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by Lola LB at 8/04 7:43 AM)
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

13 April 2012
Providing For Service Layer Objects in CFWheels
Post 2 of 3

In my previous post in this series, I made the case for why I believe that service layer objects are a common need in an application's architecture. I also pointed out the fact that CFWheels does not provide for such animals within its framework, and that I had overcome this obstacle using two different approaches. What follows are the details of my first approach and a simple yet complete working sample app for your dissection pleasure.

MANUALLY IMPLEMENTING SERVICE LAYER OBJECTS IN CFWHEELS

So, what we need to be able to do, ideally, is have and use CFCs in our controllers that are NOT directly associated with a database table. The CFWheels approach requires all models to extend the Model CFC. If we omit that extension, and then attempt to retrieve our service layer object using the "model()" method call, we receive an error. If we DO extend the Model CFC on a service layer object and attempt to retrieve it using the "model()" method call, we receive an error. What to do, what to do!

In a nutshell, we have to instantiate and retrieve our service layer objects ourselves. This could simply be done using a line in a controller that performs a "createObject" call, and that would give us our object. Ah, but one of the key elements that a CFWheels developer needs is still missing, because although I can indeed create an instance of a service layer object this way, my service layer object will be completely ignorant of the CFWheels environment. It won't be able to do one of the things it needs to, and that is to ITSELF utilize the CFWheels "model()" and "get()" calls! The remedy for this turned out to be simple, but took a LOT of digging to discover: include the appropriate files from the CFWheels framework in my service layer objects.

Personally, I opted not to show a demo of implementing SLOs (I'm gonna use this acronym from here out) the way described above, because it just wasn't beautiful nor did it provide for an easy way of re-use in multitple controllers. What I decided I wanted, then, was a method all my own that I could use within controllers in order to retrieve my SLOs. Since I retrieve models using 'model("somemodel")', I thought it appropriate to retrieve my SLOs using 'service("mySLO")'. Additionally, I didn't want my SLOs living in the same folder as my models, so I create a folder just for them called "services". This folder contains a core SLO base object, exactly the same way that the "models" folder contains a core Model object. All of my SLOs extend this core component, and thusly inherit the required CFWheels functionality they need to do their jobs.

Steps I took/Modifications I Made

1. Created a "services" folder off the root;
2. Created a core "Service.cfc" that all SLOs must extend;
3. Modified my core "Controller.cfc to include two new methods;

That's it! Let's peek at these items in more detail.

In step 2, it was convenient (and followed the same approach as the rest of CFWheels) to create a core component to be extended. The entire contents of this component is as follows:

<!---
 This CFC provides access to the wheels core functions needed by our service layer objects.
--->
<cfcomponent output="false">
 <cfinclude template="../wheels/global/functions.cfm">
</cfcomponent>

In step 3, I added two methods to my core Controller.cfc. One of these methods, you may have supposed, is called "service". The other provides a single place where the developer can declare and create all of their SLOs at once. That method is called "initServices", and it looks like this:

<cffunction name="initServices" returntype="void" hint="I initialize the services objects for this app">
 <!--- create readable alias keys that the dev will use to get these objects --->
 <cfset application.$_ServiceObjects = {
  importService = createObject("services.importUsers").init(
     dsn = get("DataSourceName")
     ),
  sessionStorage = createObject("component","services.wormhole").init()
 } />
</cffunction>

The "service" method looks like this:

<cffunction name="service" returntype="any" hint="I am the method used to access any service layer object from within any controller">
 <cfargument name="service" type="string" required="true" />
 
 <cfset var retval = "" />
 
 <cfif structkeyExists(application.$_ServiceObjects,arguments.service)>
  <cfset retval = application.$_ServiceObjects[arguments.service] />
 </cfif>
 
 <cfreturn retval />
</cffunction>

One more tiny little thing, within the Controller.cfc's Init method, I added a call to InitServices to kick it off:

<cfset initServices() />

 

 

 

 

That is IT. In this example, what I am able to do now that I could NOT do before in a CFWheels controller (without bloating my controller, anyway) is this:

<cffunction name="importUsers">
 <cfset var objImporter = service("importService") />
 <cfset result = objImporter.importData(params.dataIn) />
 <cfset flashInsert(msg=result) />
 <cfset redirectTo(action="index") />
</cffunction>

THAT, my friends, is how thin a controller method SHOULD be, WHENEVER possible!

Okay, I won't blab on about this approach. I think it's slick, it's fairly easy to maintain, and I think once you take the time to poke through and run the sample user data importer app linked in this post, that you'll agree that you really have been missing Service Layer Objects too, you just didn't call it by that name.

Doug out  :0)

Next up in the series: Implementing Service Layer Objects in CFWheels using the awesome DI framework WIREBOX and the plugin I wrote for it! :)

--------------------------------
Sample App Zip File (be sure to read the READMEDUDE.txt in the zip; you'll have to create one table and edit the datasource setting to get the app to work for you.)




Posted by dougboude at 7:39 PM | PRINT THIS POST! |Link | 4 comments
Subscription Options

You are not logged in, so your subscription status for this entry is unknown. You can login or register here.

Re: Providing For Service Layer Objects in CFWheels
This is pretty slick. Looking forward to playing around with it a bit. I'm unfamiliar with creating CFCs, so looks like I'll need to study up on that, but you've given me a good reason. Thanks! And a plugin coming soon? Even better! Maybe some practical examples of it's usage would be an idea for a future blog, something like... this is how it would have had to be done before... and this is how pretty it is after :). Just a suggestion. Thanks again!
Posted by Chris Geirman on April 16, 2012 at 2:04 PM

Re: Providing For Service Layer Objects in CFWheels
doug,

awesome post. by following your article I was able to get a service layer into cfwheels and play around with it. the problem is though that it's a little complicated to test since you have to create a controller object in order to test. I took the method you created and plae them in the following file:

service -> events/functions.cfm
initServices -> events/onapplicationstart.cfm

I also alter the initServices to automatically create object for each component in the services directory. this allows you to create services and by reloading your app, they are automatically available.

also by moving the service() method into the functions.cfm, they are available through out the entire wheels ecosystem which makes testing much easier. I created a gist for easier copy and pasting:

https://gist.github.com/2853902
Posted by tony petruzzi on June 1, 2012 at 12:43 PM

Re: Providing For Service Layer Objects in CFWheels
Hey Doug,

I got everything all setup and noticed that I am unable to access the params scope from the services. Anyway to do this without having to pass it in every time?

Thanks!

-Tim
Posted by Tim B on July 27, 2012 at 3:21 PM

Re: Providing For Service Layer Objects in CFWheels
Hi Tim! Glad you were able to get your service layer object(s) set up and working! I thought about your question over night, because it caused me to say to myself "dang, why hadn't I made provision for accessing the params???". The answer is: your service layer object should NOT be able to directly access params; this is by design, and though it's kind of a "religious" reason, I would stand my ground on this one and say that for a service layer object to have direct access to params would be outright wrong. Here's why.
One of the basic tenets of Object Oriented Programming is encapsulation; the idea that any given object should be as autonomous and stand-alone as possible, and should be as disconnected from its environment as it can be. It should never directly access scopes and values that have not been explicitly provided to it when it was called upon. Think of it in this scenario, that the service layer object you are writing should be able to be easily copied and dropped into a Fusebox app, or a Model Glue app, or a Coldbox app, and still work just as wonderfully as it does in your Wheels app. The moment you chain that service layer object to your Wheels app, it becomes a one hit wonder, a one trick pony, a bicycle with square wheels that only works great in this one very specific instance. So, although we could alter the situation so that service layer objects in wheels have direct access to params, we absolutely should not. Heck, even Wheels' own MODEL objects don't even have direct access! You have to pass in param values to a model call, right? Think of your service layer objects in the same way.
Hope that makes sense. True OOP religion does not allow us to do what you're talking about doing. :)
That said, you may want to toss it out to the Wheels list for a more well rounded philisophical discussion.
Posted by dougboude on July 28, 2012 at 3:01 AM

Name:   Required
Email:   Required your email address will not be publicly displayed.

Want to receive notifications when new comments are added? Login/Register for an account.

Time to take the Turing Test!!!

Four plus Nine equals
Type in the answer to the question you see above:

Your comment:

Sorry, no HTML allowed!