I recently hired my first employee, Jeff. If you’ve been reading my blog you may have noticed a few recent entries by Jeff. The nice thing about an employee is that you can, within the boundaries of the employees willingness to deal with it, give them whatever work you don’t want to do. Or, as in my case, don’t have the time to do.
This has, unfortunately, put Jeff in a tough spot. See, I have him working on a couple projects right now. One of these projects is an “old” Model-Glue application which I myself wrote most of a couple years ago. The application in question is a Model-Glue 1.1 application and also uses Reactor and ColdSpring.
Now, by using Model-Glue, Reactor and ColdSpring you would think that this would be an extremely maintainable application. Well this is true and not true. It’s proven quite easy to maintain the visual side of the application, but it’s turned out to be a pain to update the logical side of the application.
See, when I first started the project I didn’t have the faintest clue what a services layer was. I mean, I had a pretty good idea what a model was and I knew how to write CFCs. I’d written Reactor by then so I wasn’t completely clueless (depending on your personal perspective).
Actually, it’s important to note that I didn’t start out using ColdSpring in the application Jeff is supporting. Instead, I used ChiliBeans, which is a simple component used to configure CFCs with XML. Think of it as a very, very, very light inversion of control (IOC) component.
This was quite useful for configuring CFCs. I wrote all sorts of config components. An EmailConfig would have properties to define who sent an email and who gets BCCed when an email is sent and more. A PaymentProcessorConfig might define all the values needed to process a transaction. I think you get the point. So, yes, ChiliBeans was great for configuring some of these CFCs, but that’s all I used it for.
Somewhere along the lines I started to use ColdSpring because of Autowiring. Autowiring is a process by which, when Model-Glue starts up, it will match beans configured in ColdSpring to setters on your application’s controllers. If matches are found, the beans will automatically be set into your controller for you. This saved me a few lines of code and I quickly adopted ColdSpring.
At first I used ColdSpring as a substitute for ChiliBeans. In fact, a lot of my projects from this time period still have a Beans.xml file which was simply a merging of all my individual bean xml files from ChiliBeans. Sadly, this is what Jeff is still stuck with.
So, if I’m only using ColdSpring for simple configuration, then how do I make use of these configuration settings?
I picked up the term Fat Controllers from Jared Rypka-Hauer. A Fat Controller is a controller in an MVC application where there is too much logic (or any, for that matter) in the controller.
Let’s say, for the sake of argument, that you’re sending an email from a Model-Glue application. You’ll probably have a form that submits to an event handler. The event handler would fire a message that’s mapped to an event handler on your controller. Here’s what a hypothetical Fat Contoller method might look like:
<cffunction access="Public" hint="I send an email." name="DoSendEmail" output="false" returntype="void"> <cfargument name="event" required="true" type="ModelGlue.Core.Event"> <cfset EmailConfig=getEmailConfig() var/> <cfmail from="#EmailConfig.getTo()#" subject="#EmailConfig.getSubject()#" to="#arguments.event.getValue("from")#"> Dear #arguments.event.getValue("name")#, We thought you might want to buy our penny stocks. Etc. etc. etc. Thanks, Some Company </cfmail> </cffunction>
The getEmailConfig() method above would be set via Autowiring to a CFC configured via ColdSpring. The EmailConfig and additional values from the event are used to send an email via the cfmail tag.
But the thing is, what the heck does this buy us? About the only thing I can think of is separation of configuration from my application. However, I’m still applying procedural techniques within an OO framework. What could be more procedural than getting values and then doing stuff with them in a long line of code?
The example above is fairly simple for the purposes of this article, but from the application that Jeff’s supporting there are controller methods with 50 or more lines of code in them.
So now, a couple years after the fact, Jeff is thrown feet first into this application and asked to make some nontrivial changes. Let’s pretend that he’s working with that email controller above. Perhaps the client has decided that they want to use a Flex or Ajax form to sends the email message without reloading the page the user is on. How do you reuse the code in that controller? Well, long story short, you either put together a hack or you don’t reuse it at all.
The Service Layer
Let’s say that, instead of the controller above, we had CFC that sent an email. For the sake of argument the CFC has a method on it with this signature:
Without getting into the implementation, I think it’d be safe to say that this function performs the service of sending an email message. So let’s say that this is a “Service” CFC.
A Service Layer is made up of a collection of Service CFCs. Each service CFC has a collection methods with simple signatures that do things. They services create a faade for your application’s complex business logic. In many cases each use case in your application is matched by one method in a service.
Let’s say you have a content management system where users can add and edit content, delete content, publish content and view content. You might end up with a ContentService CFC with methods similar to this: saveContent(), deleteContent(), publishContent(), and getContent().
The nice thing about these CFCs is that they’re easily exposed to an HTML-based framework like Model-Glue, to Flex or Flash via remoting or to other languages and platforms via web services.
Getting back to our email use case, our controller could easily look more like this:
<cffunction access="Public" hint="I send an email." name="DoSendEmail" output="false" returntype="void"> <cfargument name="event" required="true" type="ModelGlue.Core.Event"> <cfset getEmailService().sendEmail(arguments.event.getValue("from"), arguments.event.getValue("name"))/> </cffunction>
Let’s ignore the implementation of the sendEmail method. It doesn’t matter. All we need to know is that if we call this method an email will be sent. But, what about all the other data that comes out of the email configuration cfc we were using before? We could easily use ColdSpring to configure the EmailService. Here’s an example configuration:
<bean class="model.email.EmailService" id="EmailService"> <constructor-arg name="from"> <value>email@example.com</value> </constructor-arg> <constructor-arg name="subject"> <value>Buy our stocks</value> </constructor-arg> <constructor-arg name="contentFile"> <value>buyStocks.txt</value> </constructor-arg> </bean>
Because the service is configured via ColdSpring it can be autowired into your controller. Heck, if you designed an API for your email service that was flexible enough, you could wire it into other controllers that might have a need to send emails from time to time.
This makes it very easier to reuse your application’s logic. It makes it more maintainable. It applies time tested design patterns. It slices, it dices, it even make julian fries.
So, the moral of the story is to put your controllers on a diet, use a Service Layer.
Comments on: "Put Your Controllers on a Diet, Use a Service Layer" (24)
I was having this conversation yesterday, and you just explained it better than I could.
Good article. One of the things that gets me as I dig deeper into programming is the *vast* range of new (and often oblique) terminology. Much like the aop section of the ColdSpring docs, this piece just explains it.
Absolutely my pleasure to help.
Great post Doug!
My rule of thumb:
As I write any new logic into a controller I always ask myself “Will other clients, like Flex, need to use this?”
If the answer is “No,” it’s in the wrong place, and needs to get out of the controller.
Very nice post! I feel like I was just lectured for doing something wrong and Im waiting for the inevitable slap on the hand from a ruler!
I am so guilty of bloating my controllers its not funny. I think this really sheds some light on one of my major gripes with Fusebox. I love Fusebox, have for over 7 years now. But, I hate writing controllers in XML since I always fall into putting logic in those controllers. I use a service layer along with DAO’s and gateways but I only use my service layer for interacting with those other cfc’s.
This makes me realize that I need to move that logic out of the fusebox controller and into the service layer. Thanks Doug! Now I have to go ask my wife to slap my forehead!
@Joe – is that the wrong way round?!
Usually I ask whether other consumers will need to access functionality, if the answer is “no” (i.e. that only the HTML app needs to access it), then I may put it in the controller. If the answer is yes, I will definitely put it in the model in a service layer to provide a consistent model API.
I typically have at least two consumers: the primary UI medium (HTML, Flex, whatever) and unit tests.
On a practical, non-theoretical level, it’s usually a minimal amount of work to go ahead and put logic in the model or in a service layer. That way, you’re anticipating change and reuse of logic in other contexts, and you’ve made the logic testable.
It’s a small investment that can have a big payoff.
I disagree with these concepts. IMO you’re talking about model code, not controller code. Hell it could be easily argued that a cfmail is not even model code, it’s actually view code since the user is seeing the results. But in my opinion it is absolutely, positively is not controller code.
This big push for having both a service layer AND a controller layer in my opinion is redundant. What you have displayed as a ‘service’ layer should be the controller layer. For that matter it’s a GOOD controller because it’s just calling other code in the application (i.e. model or view code). That’s what a controller should do.
OK, I must just have misread your initial comment then.
I read that as saying that if other clients didn’t need to access it, you would put it in the model which would suggest that if other clients DID need to access it, you would put it in the controller which was what confused me.
Personally I put all non-HTML specific logic into the service layer. My boundary is that the controller handles things like the form and URL scope, exchanging information with the service layer in a way similar that that which would be done by a flex app, AJAX or web service (with a small model adapter to handle the various elements that are unique to each class of client).
So I’m guessing we’re saying the same thing based on your second comment (which doesn’t surprise me), I’d just change the word “no” to “yes” in the last line of your original comment to express that shared intent – no?!
@Steve, but if you put it into an HTML aware controller that expects page requests, it can’t be easily reused by Flex, AJAX and web service calls that don’t necessarily have a page based metaphor for their communications. Personally I have a notification service in my model allowing objects to easily implement multi-channel notifications and I either explicitly call it or use a publisher/subscriber model to allow for decoupled management of notifications within an application if you have model objects that you want notified about but which don’t need to be “notification aware”.
@Peter, right, no. I mean yes. You’re both saying the same thing. At least, you’re both saying what I was going to say.
@Steve – take for instance an application we built recently for a particularly annoying client. It had both HTML and a Flex front-ends. Some of the functionality was specific only to the HTML front-end (namely, importers, admin code, some of the checkout process). So we casually put logic in the HTML controller (which at the time was Fusebox) and that decision didn’t bite us. Likewise, some functionality was specific to Flex-only. It wasn’t a tough decision to put code to create that kind of functionality directly into the CFC facade layer which Flex accessed.
However, some of the code was shared by both, so we used a service layer (although we didn’t name the CFCs “BlahBlahService.cfc”).
@all, The thing about service layers is that they really make maintaining code a lot easier. If you put too much logic in your controllers it’s a lot harder to refactor cleanly.
For example, let’s say that before I send an email I want to validate the format of the data and let’s also say that when I first write this code I put the validation and the email-sending in one controller method.
Now, a while later I realize that I could reuse that validation logic elsewhere, or maybe I need different validation before sending a different email. How do I reuse the relevant code that’s in my controller? Well, I could refactor it into a private method on my controller and then just call that method any time I need to.
This begins to have its own problems too though. For example, it seems inconsistent to have a bunch of methods on a controller that deal with shuffling data around and another odd-ball method for doing something.
If you’re an OO purist you might also point at that, at at that point, your controller stops being just a controller, but also an emailer or validator. As far as separation of concerns is concerned that’s not necessarily a good thing.
Also, Let’s say you have more than one controller. And your other controllers need to send an email. What would you do? Create a new instance of this other controller and call that one private method on it?
How is it not easier to simply have your controllers call a method on a clear and concise method on an object?
Also, if you consider unit testing, having this one method on a service is a lot easier to test than figuring out how to create and pass events into your controller and then watching how the values in the event change.
All in all, to me this makes sense (but only after a lot of pain and suffering). To others maybe it doesn’t. Do what you believe is best.
Peter, what are you talking about? I do that all the time. A single CFC can talk to both an HTML UI and a JS UI and a flex UI. CFC doesn’t give a hoot who’s requesting it.
Nat, that application in effect had two different frameworks which was silly. No offense, but it was VERY hard to follow. We could have achieved everything we did with cfcs and no Fusebox XML layer. The controller methods that flex or Ajax calls would likely call a model method(s) and display the raw data directly (cfreturn is really just a cfoutput of xml). The controller methods that the HTML pages call may call the exact same model methods then cfoutput some HTML. Why would the fact that one method displays XML and another displays XHTML make one method a “controller” and another a “facade”? It wouldn’t in my opinion.
Those two methods can exist in the same CFC or in their own if you want. But regardless, they are both controllers because they are just calling other logic, not running code directly.
“@all, The thing about service layers is that they really make maintaining code a lot easier. If you put too much logic in your controllers it’s a lot harder to refactor cleanly. “-Doug
You’re confusing a controller with a model. Remember this is MVC not just VC or C. There should be almost NO code in a controller method. The only code should be flow of control code. i.e. cfinvoke, cfset cfif, maybe cfloop. But IMO tags like cfmail, cfquery, cfhttp, cfform etc. should NEVER be in a controller. Those should go in either model or view CFCs.
Think about it. If you put any logic in a controller, why bother having model CFCs? Or… if you put any HTML in a controller CFC why bother having view CFCs?
This whole concept of a service layer replacing a controller is redundant unless you want to just rename ‘controller’ to ‘service’, which doesn’t gain you anything.
“Steve, but if you put it into an HTML aware controller that expects page requests, it can’t be easily reused by Flex, AJAX and web service calls that don’t necessarily have a page based metaphor for their communications.” – Peter
Consider this Peter… first the HTML version:
/model/users/users.cfc method: “getUser”
-this runs a cfquery and returns the recordset
/view/users/users.cfc method: “ListUsers”
-this needs a recordset of users and displays them in HTML. It knows nothing about the model method “getuser” it just wants the recordset.
/controller/users/users.cfc method: “ListUsers”
That is going to run the model method getusers then run the view method listusers, passing the recordset into the list users.
Now on the ajax/flex side, the UI exists somewhere else, all we want is that same “getUsers” recordset…
/controller/users/users.cfc method: “getUsers”
Or you could encode it in json if you wanted.
This is hard to describe in a comment box. I’m curious if my post will even go through. 🙂
damn. It didn’t go through. I had cf code in there, it got stripped out. oh well.
In a nutshell, the HTML code and the ajax/flex code can both call the same model method.
Steve said: “The only code should be flow of control code”
That just about sums it up for me.
Steve – I think we’re saying the same thing after all. I’m saying you shouldn’t have stuff in your controllers and apparently so are you.
“You’re confusing a controller with a model. Remember this is MVC not just VC or C. There should be almost NO code in a controller method. The only code should be flow of control code. i.e. cfinvoke, cfset cfif, maybe cfloop. But IMO tags like cfmail, cfquery, cfhttp, cfform etc. should NEVER be in a controller. Those should go in either model or view CFCs.” <— that sums up exactly what I (think I) said.
Steve – Are you the guy who did the “CFCs are the Framework” presentation at the frameworks conference? If so, I get what you’re saying now.
Let’s say I want to add a new user record in an HTML application. Something has to take form.FirstName and pass that to the model. That (for me) is the controller (and in my system it’s all generic base classes). Of course, your model could “know” about the form scope, but then what happens if you get a SOAP request with an XML doc containing the FirstName within that?
Doug, yeah, that was me. There is some code from my last presentation at: http://labs.webapper.net
Peter, Yes, it is a controller, but the controller itself shouldn’t contain the html form, it should invoke a method in a view which contains the form.
Then when you submit the form, you’d submit it to a controller method. That method shouldn’t contain any queries etc, it should invoke method(s) in the model which contain those queries. After that cfinvoke, if it’s an HTML app it would probably do a cflocation to another controller to display the HTML result. Whereas if it’s an Ajax app, it would like return/cfoutput some XML/json data back to the browser.
Another useful ajax technique is to have the XmlHttpRequest make a request to a controller method, that invokes a view method and outputs HTML, then display that in a div. i.e. somediv.innerHTML=httprequest.responseText;
That’s kind of a mixture of traditional web app and an ajax app.
I have a question, first let me say I am new to frameworks and I am using Model-Glue Unity, so far I love it. I would like to put my controller on diet but not sure how to go about it, here is what I have:
Controller Function called processOpenProduct “Which allows users that don’t have a membership to our site buy products”
So the function add the user to our database returns there ID and then processes the transaction once the transaction is processed it adds a record to the transaction table and adds as many records needed to the transactionDetail table then sends an email.
So my question should I just pass the event to the transaction service and have the transaction service call the other services to process the transactiondetails and the users?
I hope all of this make sense.
Great discussion. I totally agree with the sentiment, but I think the way it’s expressed is sometimes a little extreme.
Saying there should be *no* logic in the controller is a bit disingenuous. An “if…then” is logic – are we saying we shouldn’t use “if” in controller code?.
As is the statement that being able to graft another front end onto the same controller is a general design goal. Yes, its a nifty thing to be able to do, but in practice it just promotes lowest common denominator UIs.
Finally, if we accept the idea that we want to be able to deal a number of front ends with the same controller, all that means is that we have a generic controller. By all means move anything that’s not to do with application flow out into a service layer or the model, but it’s a bit of a leap from there to the notion that the controller should be logic-free.
I had a feeling when he said “logic,” it refers to “business logic.” Of course there is logic in controllers, but it should be confined to flow-of-control logic.
I could be wrong, of course.