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,246 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,246 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,246 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 25, 2013 at 10:01 PM
My Experience With AngularJS - The Super-heroic JavaScript MVW Framework
@Avi, Really glad to help! @Jaredwilli, I'm finding a this image hits home with a lot of people :) Hopefully we can all work through the rough patches together! @Prateek, AngularJS has error ... read »
May 25, 2013 at 9:53 PM
Nested Views, Routing, And Deep Linking With AngularJS
@Mrsean2k, I'm glad I could help! I haven't been able to keep up with the ui-router stuff. I keep saying that I'll carve out time, but I just haven't gotten to it :( ... read »
May 25, 2013 at 9:49 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, Thanks for the book recommendations. I am looking them up right now. I can see that Object Thinking is available for the Kindle App - sweet! Also, I just recently heard Martin Fowler on the ... read »
May 25, 2013 at 9:41 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
@Chris, I'm super excited to hear that my posts are helpful. I am also loving AngularJS; but, it definitely has some caveats and some odd behaviors and some things that just don't seem to "wor ... read »
May 25, 2013 at 9:36 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam, @Jason, After reading these comments, I double-checked my latest implementation and I am happy to report that I am using listFirst() and listRest(). ... read »
May 25, 2013 at 9:31 PM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Daxesh, I am not sure I understand the question about the current node. If you already have a reference to the current node, why would you need to query for it? As for parent node, I believe that ... read »
May 25, 2013 at 10:08 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
@Ben, my question is that i want the current node with its tag and its parent node. i just want only that data. So, give me the solution for that. and remember solution is working on " xpath 1.0 ... read »
May 25, 2013 at 10:01 AM
Using "//" And ".//" Expressions In XPath XML Search Directives In ColdFusion
hey ben, i want get my current node tag and also want the root node tag withing. So, how can i fix it.. ! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools