Using A Sandbox To Decouple ColdFusion Components In A Modular Application Architecture

Posted April 12, 2012 at 5:39 PM by Ben Nadel

Tags: ColdFusion

In the last few posts, I looked at using Closures in ColdFusion 10 to limit access to a component's methods. This path of exploration was based on a Crockford presentation that I saw on JavaScript. Along the same lines, I've read a good number of blog posts that describe using a "Sandbox" as a way to keep JavaScript modules decoupled from each other as well as loosely coupled to the application in which they reside. I've never really used a sandbox object in JavaScript or ColdFusion; so, I thought this would be a fun learning extension of the previous two posts.

NOTE: At the time of this writing, ColdFusion 10 was in public beta.


 
 
 

 
  
 
 
 

Typically, in a ColdFusion application, I pass component references into the init() method of any component that needs them. So if component-B needs a reference to component-A, I'll pass component-A into component-B at the point of instantiation:

  • componentB = new ComponentB( componentA );

This helps keep my ColdFusion components loosely coupled from the application because they never break encapsulation when accessing functionality; that is, they never have to reach beyond their instantiation arguments to invoke non-local functions. This is nice; but, it still couples one component to another. Not only do we have to pass-in all the required components, we have to make sure that the required component APIs don't change.

A Sandbox object can alleviate both of these problems. A Sandbox object allows for method renaming as well as the merging of multiple points of functionality. As far as I understand it (which is really very little at this point), a Sandbox object is a component's only point of contact with the outside world: the component can only talk to itself; or, it can talk to the Sandbox.

To explore this architecture, I have a BlogService.cfc ColdFusion component that requires several other components in order to function properly. And, as you'll see in the code below, I never pass those dependencies into the BlogService.cfc instance; rather, I create a Sandbox object that proxies the dependencies, providing a single reference and single API.

  • <cfscript>
  •  
  •  
  • // Set up the domain model for some of the components. For this
  • // demo, let's assume that these components are super simple and
  • // don't need any special configuration.
  • securityService = new SecurityService();
  • emailService = new EmailService();
  • logService = new LogService();
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Next, we're gonna set up the Blog Service domain model. For
  • // this, we're going to set up a sandbox object - this is the
  • // Blog Service's connection to the world outside itself. It is
  • // the only way that the blog service can interact with the
  • // application at-large (of which it really doesn't know
  • // anything).
  • sandbox = {};
  •  
  • // Set up a sandbox method - pass off to Security Service.
  • sandbox.isBlackListedComment = function(
  • targetName,
  • targetUrl,
  • targetIP
  • ){
  •  
  • return(
  • securityService.isBlackListedName( targetName ) ||
  • securityService.isBlackListedUrl( targetUrl ) ||
  • securityService.isBlackListedIP( targetIP )
  • );
  •  
  • };
  •  
  • // Set up a sandbox method - pass off to Email Service.
  • sandbox.sendCommentEmail = function( users, subject, content ){
  •  
  • return(
  • emailService.sendBlogCommentEmail( users, subject, content )
  • );
  •  
  • };
  •  
  • // Set up a sandbox method - pass off to Log Service.
  • sandbox.logComment = function( name, comment ){
  •  
  • return(
  • logService.logBlogComment( name, comment )
  • );
  •  
  • };
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Instantiate the Blog Service and pass-in the Sandbox object
  • // that we created above; this sandbox allows the blog service
  • // to communicate with the greater appliation when necessary.
  • // BUT, only the most limited way possible to do the job.
  • blogService = new BlogService( sandbox );
  •  
  • // Save a blog comment - this process may involve calls to the
  • // sandbox object for security checks, logging, and email.
  • blogService.addComment(
  • name = "Tricia",
  • email = "tricia@test.com",
  • comment = "Hey Ben, what it be like?!"
  • );
  •  
  •  
  • </cfscript>

Due to limitations in the ColdFusion 10 parser / compiler, I cannot instantiate my BlogService.cfc ColdFusion component and define the Sandbox object in one step; rather, I have to define the Sandbox object first and then pass it into the BlogService.cfc during instantiation.

As you can see, though, the Sandbox object doesn't really provide any "new" functionality; rather, it acts as a proxy to several other components. This layer of abstraction completely decouples the components from each other - now, the BlogService.cfc is only coupled the Sandbox. This means that as the dependencies change, the internal logic of the Sandbox can be changed to shield the BlogService.cfc.

I think that this probably makes the creation, testing, and maintaining of ColdFusion components easier because you only have to worry about one point of contact - you only have to worry about one point of coupling. The downside is that it definitely requires more code and more forethought about how your ColdFusion components will work.

I'm really outside my comfort zone here, in terms of application architecture; so, all responses, good and bad, are definitely welcome.



Reader Comments

Apr 12, 2012 at 6:51 PM // reply »
45 Comments

Ben,

This is interesting as an exercise for playing with the new features, but personally, I'd argue against this practice (at least certain aspects of it).

The method injection is actually a pretty neat feature, and runtime method injection is something that a lot of people use and swear by.

The biggest issue I would take with the above code is that you really should have a concrete Interface defined to pass into the BlogService constructor, and the sandbox object should conform to that interface. The reason this is necessary is that if you don't define the interface, then you can pass ANY object to the BlogService. More importantly, you could pass an object to the BlogService that is missing methods that it needs. This leads to a situation where in your calling code, you need to use reflection (which is easy in CF) before calling any method which has been generated in this manner.

I know you can already delete methods on objects and break things pretty easily in CF, but in this case, where you are generating objects and adding and removing methods as a modus operandi, it could become more pronounced. If you really wanted to ensure the contract between the objects, you would be forced to do it at runtime, with code that looks like this:

  • component BlogService;
  •  
  • init(sandbox) {
  • variables.sandbox = sandbox;
  • }
  •  
  • function addComment(name, email, comment) {
  • if (structKeyExists(sandbox, "isBlackListedComment") and isCustomFunction(sandbox.isBlackListedComment)) {
  • sandbox.isBlackListedComment(comment);
  • //....
  • }
  •  
  • if (structKeyExists(sandbox, "sendCommentEmail") and isCustomFunction(sandbox.sendCommentEmail)) {
  • sandbox.sendCommentEmail(comment);
  • //....
  • }
  •  
  • //etc....
  • }

You wind up paying the price pretty heavily in your BlogService for being able to dynamically rearrange your objects.


Apr 12, 2012 at 10:10 PM // reply »
11,241 Comments

@Roland,

I definitely see what you're saying. I wouldn't be against moving the "Sandbox" into its own CFC. If for no other reason, it would definitely make the code smaller. In my example, I am only dealing with *one* component. Now, imagine that inline-sandbox-creation for a dozen components?


Apr 12, 2012 at 11:42 PM // reply »
4 Comments

Would anyone be able to provide some good reference material talking about code design following this thought logic?


Apr 13, 2012 at 12:00 PM // reply »
5 Comments

This is a cool concept, but I'm having trouble imagining how I would use it. If I understand correctly, it seems like the sandbox is ultimately providing an API to your securityService, emailService, and logService APIs?

I agree with CJM, I'd like to see some more information about designing code in this fashion ( in any language).


Apr 14, 2012 at 1:55 PM // reply »
11,241 Comments

@Sean, @CJM,

I don't personally have any real-world experience with this kind of programming, either on the server-side or the client-side. The only place I've ever seen it really discussed in any detail is for client-side programming. Maybe this is because UI-based modules are made to be more modular / reusable than client-side modules? Perhaps that requires a larger separation of concerns.

Here is an article by the great Addi Osmani that discusses large scale JavaScript application architecture. He touches on Sandbox usage:

http://addyosmani.com/largescalejavascript/

Nicholas Zakas also mentions this stuff in a number of articles / presentations, but I cannot find a link at this time.

All to say, for me, this is just experimentation. I can't really argue pros/cons from a real-world standpoint. As always, definitely open to any ideas.


Apr 15, 2012 at 1:04 AM // reply »
5 Comments

@Ben,

Thanks for the link, I love Addy Osmani's stuff and this one is no exception. The "sandbox" pattern was having trouble clicking with me until I saw him call it by another name - "facade."

Because I work primarily with server-side stuff, I'm used to seeing the facade as an individual class rather than being defined the way you did it.

What I think is really cool about this demo - although I'm still having trouble coming up with a use case - is that you've essentially created a CFC (commentFacade AKA sandbox) without actually having to define it in a physical file (commentFacade.cfc).


Apr 16, 2012 at 12:36 PM // reply »
10 Comments

The example given is really just a Facade / Adaptor / Flyweight design pattern.

Basically, object A expects to talk to an object of type B, but you only have an object of type C, so you create a "facade" that looks like a B, but translates parameters and results to and from C.

BTW, if you haven't read Design Patterns: Elements of Reusable Object-Oriented Software, you should drop everything and do it now... (grin)

(Tried to provide link, commenting system blocked it...)


Apr 17, 2012 at 10:53 AM // reply »
11,241 Comments

@Sean, @Michael,

I did read the Design Patterns book years ago; way before I really knew much about anything. I should probably read it again now that my view on the world has changed so much and I have a few years under my belt.

In addition to simplifying things, which I think is what a Facade does??? I like the fact that this completely decoupled one component from the other components that are hidden behind the Facade. Just seems cool!


Apr 17, 2012 at 11:25 AM // reply »
10 Comments

@Ben, a Facade traditionally presents a simpler or easier to use interface.

An Adaptor is used when an object (A) expects to use an object with a certain interface (B), and the object you have (C) doesn't conform.

The Adaptor wraps a B interface around object C, translating the requests and results from and passed back to object A.

That's what you implied with, "This means that as the dependencies change, the internal logic of the Sandbox can be changed to shield the BlogService.cfc."

If you're going to do it, however, you should spend some thought on what might be an optimum implementation. I've seen too many Adaptors where you're expected to pass in the specific structures and constants required by a specific implementation, and which make actually changing the underlying implementation all but impossible.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
May 21, 2013 at 7:46 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
No luck. At least I have uncovered the cause, URLScan 3.1. Here is what I see in the IIS log when a file is over 30mb. 2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8. ... read »
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
May 21, 2013 at 11:51 AM
Ask Ben: Parsing Very Large XML Documents In ColdFusion
Looking at my first ever XML document that I have to parse and put into MS SQL 2000 with CF8. I get it to list the desired Field name, many times over, and have a long list of this field name displa ... read »
May 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
May 20, 2013 at 4:29 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, That's if you're trying to reference a specific row. In this case, we're trying to reference the entire query column as one cohesive value. So, you are correct that if you wanted to output a ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools