Accessing the session scope from CFCs
I've just discovered a great post by Peter Bell about the issue of accessing the session scope in OO applications. This post doesn't add anything new (apart from some code samples) but it just clicked with me so thought I'd share.
Basically there are three options when dealing with session and cfcs (Please note this code is trimmed down for ease of reading - don't forget to add output="false" to your cfcomponent and cffunction tags).
1) Just reference the session scope in your CFCs
<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfreturn StructKeyExists( session, "userid" ) />
</cffunction>
</cfcomponent>
This works, but it does mean that we are accessing a scope that isn't injected into our object. Not a huge problem unless you have more than one cfc referencing the session scope and then decide you want to store the user in as a cookie. That's just going to be end up with a big mess.
2) Create a cfc which handles setting and getting session values for your whole application.
<cfcomponent name="SessionFacade">
<cffunction name="setKey" returntype="void">
<cfargument name="key" />
<cfargument name="value" />
<cfset session[ arguments.key ] = arguments.value />
</cffunction>
<cffunction name="getKey" returntype="any">
<cfargument name="key" />
<cfreturn session[ arguments.key ] />
</cffunction>
<cffunction name="IsKeyValid" returntype="boolean">
<cfargument name="key" />
<cfreturn StructKeyExists( session, arguments.key ) />
</cffunction>
</cfcomponent>
<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfset var SessionFacade = CreateObject('component', 'SessionFacade').init() />
<cfreturn SessionFacade.IsKeyValid( "userid" ) />
</cffunction>
</cfcomponent>
Hurrah - problem solved, we've moved the responsibilty of getting and setting the session variables to our new class. Not so fast! What happens if we decide to store the userid in the cookie scope, we now need to go off and create a CookieFacade. The Authentication class still needs to know the scope (session or cookie) so that it can call the correct class (SessionFacade or CookieFacade) we really haven't gained anything.
3) Create a scope agnostic class.
<cfcomponent name="SiteUser">
<cffunction name="setUserId" returntype="void">
<cfargument name="value" />
<cfset session[ "userid" ] = arguments.value />
</cffunction>
<cffunction name="getUserId" returntype="numeric">
<cfreturn session[ "userid" ] />
</cffunction>
<cffunction name="IsUserAutheticated" returntype="boolean">
<cfreturn StructKeyExists( session, "userid" ) />
</cffunction>
</cfcomponent>
<cfcomponent name="Authentication">
<cffunction name="IsUserLoggedIn" returntype="boolean">
<cfset var SiteUser = CreateObject('component', 'SiteUser').init() />
<cfreturn SiteUser.IsUserAutheticated( ) />
</cffunction>
</cfcomponent>
It's a subtle change, but now we could easily change our SiteUser class to use cookies and our Authentication class need never know.
As an additional note, as Sean Corfield points out in the comments of Peter's post, you could still have a SessionFacade and a CookieFacade which SiteUser uses.
I'd also like to thank Bob Silverberg, Adrian Moreno and Brian Kotek for pointing me in the right direction (hopefully I've got it right!).
- Posted in:
- ColdFusion
- OOP


Comment by Dan Vega – January 23, 2009
Comment by John Whish – January 23, 2009
Comment by Bim Paras – January 23, 2009
Comment by Michael Brennan-White – January 23, 2009
Comment by John Whish – January 23, 2009
I often use a SessionFacade, but my usage of it appears to be quite different from what most people talk about when this topic comes up. First, I don't use a generic object with a generic getter and setter than anything can use to dump anything into the shared scope. I limit what is available to only what is needed, i.e. getUser, getShoppingCart, etc. So in that way it does seem much closer to your SiteUser.
Second, everyone assumes that calling it a "session facade" is somehow bad because it binds it's purpose to ColdFusion's session scope. I don't see it this way. ColdFusion happens to have a scope called "session" that is one way to persist information across HTTP requests. But it is not the only way, cookie and client also come to mind. When I call this object a SessionFacade, I don't mean it as "facade to the session scope", but rather as "facade to whatever handles dealing with data that exists across multiple HTTP reqeusts, which is typically called an HTTP session".
I realize that I'm probably in the minority on this, and that the naming is can be seen as ambiguous, but I still find that using it allows for much easier unit testing since it becomes simple to mock the object and allow tests to run without having to create an entire application/session/client/cookie setup around it. Add to that the fact that I typically am dealing with a very limited set of data in the session scope, which is only referenced from an equally limited number of service object methods, and the end result is that, for me, the pros outweigh the cons.
That's my story and I'm sticking to it! ;-)
Comment by Brian Kotek – January 23, 2009
Comment by Allen – January 25, 2009
I know exactly what you mean about naming conventions! I wanted to call it Session (as in user session), then it was BrowserSession (as session/cookies are browser dependant). Then it became Request, I've finally ended up calling it ScopeFacade (although I'm not convinced it should even have 'Facade' in the name!). Although reading your comment again "Shared Scope" pretty much describes what the class is for, so maybe I should use that! :)
@Allen, yep, I should have a var keyword before "SiteUser" in my class. I'll update the post. I just typed the code directly into my blog - I really must check it! As for locking, I didn't foresee any issues with race conditions in this particular case. There is an excellent post here on cflock:
www.coldfusionguy.com/ColdFusion/blog/index.cfm/2008/1/11/Understanding-CFLock
Comment by John Whish – January 26, 2009
Comment by Allen – January 27, 2009
/sharedCFCs
---/sessionMamagement
--------SiteUser.cfc
---/otherCFCs
--------someCFCCallingSiteUser.cfc
/www1
----Application.cfc
----page1CallingSiteUser.cfm
/www2
----Application.cfc
----page2CallingSiteUser.cfm
As you can appreciate, there is no Application.cfc/cfm in "/sharedCFCs" dir. I am wondering if such code will treat sessions well. Will someCFCCallingSiteUser.cfc get the right session if invocated by some page from www1 dir? Will page1CallingSiteUser.cfm and page2CallingSiteUser.cfm work properly? Or I just get the whole thing wrong? Hope to hear your oppinion.
Comment by Jack – March 10, 2009
Does that help?
Comment by John Whish – March 10, 2009
That means scope-agnostic SiteUser.cfc has to be located within the scope-aware application namespace to work properly. If located anywhere else - the logic of maintaining persistent data has to be programmed and maintained repeatedly within the code located under Application.cfc/cfm.
If I am correct, there is a lot of code rewriting waiting for me :) Thanks again for your comment!
Comment by Jack – March 10, 2009
Comment by John Whish – March 11, 2009