A few weeks back I posted an entry about what I perceive as a "skill divide" between advanced CF developers and more entry-level developers. The post generated a huge number of comments, and I'd like to thank all of the people who read and shared their thoughts on this.

Some of the comments mentioned that part of the problem is that newer folks just don't know or understand what the point of some of these so-called "advanced techniques" are. For example, one might say "use ColdSpring, an inversion-of-control container that helps you wire together your CFCs and manage the dependencies between your components". The response from a newcomer might be "Inversion of what?" or "My CFCs don't have any wires."

So I thought I'd make an entry that talks specifically about ColdSpring and tries to explain what it is and why you might want to use it.

Consider the following UML diagram. If you aren't familiar with UML, don't worry, it's just a way to visualize the relationships between objects.

In summary, what this shows is that we have a ProductService object as the sort of "top-dog" in this small set of objects. The ProductService is composed of several other objects (this is called "object composition", and is also known as a "has-a" relationship):

  • a ProductFactory, to create individual instances of a Product
  • a ProductGateway, to handle calls to the database to return aggregated product data (perhaps all products in a given category)
  • and a ProductDAO, to deal with creating, reading, updating, and deleting a single product from the database

The ProductGateway and the ProductDAO, in turn, each contain a Config object. The Config object might encapsulate the datasource name, password, etc., so that the DAO and the Gateway can properly query the database.

Now whether or not you completely understand what DAOs or Gateways or composition are, I'm sure we can all agree that a set of dependencies exists between these CFCs. The Service depends on the Factory, DAO, and Gateway. The DAO and Gateway depend on the Configuration object. If any of these dependencies are not obeyed, there are going to be big problems. Whenever some external code (like a Fusebox or Model-Glue controller) tries to use the ProductService, everything has to be "wired up" correctly or there will be an error.

So. Dependencies. No problem right? We just need some nice code to create things in the correct order and make sure each CFC gets what it needs. I want to keep the ProductService in the application scope, so I might create code like this to do what I need:

<cfif not structKeyExists(application,'appInitialized')>
	<cflock name="appInitLock" type="exclusive">
		<cfif not structKeyExists(application,'appInitialized')>
			<cfset config = createObject('component','components.Config').init('/config/configFile.xml') />
			<cfset productGateway = createObject('component','components.ProductGateway').init(config) />
			<cfset productDAO = createObject('component','components.ProductDAO').init(config) />
			<cfset productFactory = createObject('component','components.ProductFactory').init() />
			<cfset application.services.productService = createObject('component','components.ProductService').init(productFactory,productDAO,productGateway) />
			<cfset application.appInitialized = true />
		</cfif>
	</cflock>
</cfif>
		

That works, everything is built up in the correct order, and I can ride off into the coders sunset, right?

Well, maybe. This should work for now. But that is sort of complicated isn't it? And if I create anything in the wrong order, or pass the wrong CFC, everything goes boom. Now imagine the app gets bigger. There might be 10, 20, 30 CFCs being created and passed around. I'd better keep a close eye on what I'm doing!

Oh but that's right, I don't need to care! I can use ColdSpring to manage my dependencies. Here's some XML that would tell ColdSpring to wire up my CFCs in the proper way:

<beans>

	<bean id="productService" class="components.ProductService">
		<constructor-arg name="productFactory">
			<ref bean="productFactory" />
		</constructor-arg>
		<constructor-arg name="productDAO">
			<ref bean="productDAO" />
		</constructor-arg>
		<constructor-arg name="productGateway">
			<ref bean="productGateway" />
		</constructor-arg>
	</bean>
	
	<bean id="productDAO" class="components.ProductDAO">
		<constructor-arg name="config">
			<ref bean="config" />
		</constructor-arg>
	</bean>
	
	<bean id="productGateway" class="components.ProductGateway">
		<constructor-arg name="config">
			<ref bean="config" />
		</constructor-arg>
	</bean>
	
	<bean id="config" class="components.config">
		<constructor-arg name="configFile">
			<value>/config/configFile.xml</value>
		</constructor-arg>
	</bean>
	
	<bean id="productFactory" class="components.ProductFactory" />
	
</beans>
		

Using this XML, ColdSpring knows what each CFC needs and it knows what order the CFCs must be created in. I can tell ColdSpring to cache my instances into the application scope (it does this by default), or not to. In fact, just about any situation you can think of when it comes to creating a set of objects, ColdSpring can tackle. It's really a sweet bit of engineering. Hats off to Dave Ross and anyone else who is contributing.

I hope that this post has made it more clear what "wiring together" your CFCs means, and why you might need to do this. I invite you to check out the full details on ColdSpring at http://www.coldspringframework.org/ and investigate its full feature set, because I've only scratched the surface.

Comments Comments (24) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 14601 Views

Related Blog Entries

Comments

  • # Posted By Brian Kotek | 5/31/06 6:22 PM

    Oh and of course, post any comments here! Thanks.

  • # Posted By tof | 5/31/06 7:51 PM

    Ahhhhhh. Thanks for this article man, it's much clearer now. Coldspring is some kind of foreign-key constraints manager... for objects that is, and working the other way around :-D.

  • # Posted By Sean Corfield | 6/1/06 2:47 AM

    Also have a look at my "Managing ColdFusion Components with Factories" presentation:

    http://corfield.org/articles/cfobj_factories.pdf

  • # Posted By Trond Ulseth | 6/1/06 3:14 AM

    Great article Brian. Thanks a lot for this one. For the first time I feel like I understand (partly at least) what ColdSpring is about. After reading this I also understood (again - partly at least) Sean's article (thank you to you also Sean) which I've read before, but then not understanding what I was reading.

    I guess the route now would be to try and use the service and factory approach, and when "getting that" move over to ColdSpring.

  • # Posted By Kyle Hayes | 10/1/06 12:26 AM

    Brian, as with the others I agree it is a good brief article on how Coldspring can help you manage your dependencies. Not to be critical, but I think that it would also be beneficial to an OO programmer to see how it fits into the aspected-oriented programming methodology and what aspects do for organization of code. It would be awesome if you could somehow drop a basic explanation of this into your article. Or you could simply link to Wikipedia on AOP. Thanks for the post.

  • # Posted By Patrick McElhaney | 11/2/06 2:59 PM

    I consider myself a VERY advanced ColdFusion developer and I still don't get ColdSpring.

    In your example you replaced 12 lines of code with 30 lines of code. Sorry if I'm a little thick, but I fail to see how that is better.

    Furthermore, I know exactly what the first example is doing, because it's ColdFusion. The second is another language. It's a simple, pretty intuitive language, but another language nonetheless.

    How are two languages easier to maintain than one language?

  • # Posted By Brian | 11/2/06 3:25 PM

    Patrick, lines of code is not a very good indicator of complexity or efficiency. Yes, the ColdSpring example is technically more lines of code. But those lines of code are much more simple and straightforward. I think if you're basing your interpretation of "better" simply on LOC then we definitely have differing definitions of better.

    Not only is the ColdSpring XML a full map of your model's dependencies, but it handles the order of creation for you, will autowire your framework for you, offers AOP, remote facade generation, lazy loading, and much more. You basically get a huge number of benefits for virtually no cost.

    I know you've been a CF'er for a very long time Patrick. But since just about every high-profile CF'er that I can think of not only uses ColdSpring but raves about it, I'd ask you to consider the possibility that you might indeed be missing something when it comes to the benefits of ColdSpring. And while I would never say that just because others are doing it that they must be right, I would hope that this would encourage serious consideration on your part. Have you actually tried it out? As with other frameworks, the benefits really manifest themselves on a large scale. When you are dealing with dozens or hundreds of CFC dependencies, ColdSpring really becomes a huge benefit.

  • # Posted By Patrick McElhaney | 11/2/06 5:02 PM

    Yes, number of lines != complexity, as anyone who's ever debugged Perl knows. But it's usually a pretty good place to start.

    As far as I can tell, the ColdSpring has three advantages:

    1. The framework handles all the appication initialization stuff, so I don't have to write code to do so.

    2. The tags in the ColdSpring example don't have to be in any particular order, whereas the statements in the CF-only example do generally need to flow from innermost to outermost objects.

    3. ColdSpring doesn't initialize anything until it's needed. (That's not apparent from your example, but I think I've heard that before.)

    If I'm missing anything, please let me know. But since we're having the conversation asynchronously, I'll go ahead and address the three points above.

    1. Seems to me that CS is swatting a fly with a sledge hammer. Couldn't you just wrap the CF code above in a command object and pass that to a tiny little framework that initializes your app when needed?

    2. I think the inner-to-outer constraint makes the dependencies easier to follow.

    3. Since these objects are all singletons, what's the harm in creating them before they're needed?

  • # Posted By Patrick McElhaney | 11/2/06 5:06 PM

    Heh, I just realized how old this blog post is. For some reason Google Reader decided I'd like to read it again. :)

  • # Posted By Patrick McElhaney | 11/2/06 5:10 PM

    "I'd ask you to consider the possibility that you might indeed be missing something when it comes to the benefits of ColdSpring"

    BTW, just in case it wasn't clear, that's /exactly/ what I'm trying to do. :)

  • # Posted By Kyle Hayes | 11/2/06 5:30 PM

    Hey Brian. I know at first it doesn't seem like ColdSpring does much, but take a look at my Google Notebook on Aspect-Oriented Programming (http://www.google.com/notebook/public/114821308727...). It will give you an insight on what ColdSpring is trying to accomplish in your projects.

  • # Posted By Sean Corfield | 11/2/06 6:41 PM

    Patrick, other benefits of ColdSpring: it can manage circular dependencies (which crop up more often than you might think in OO systems); it can transparently proxy objects (e.g., to add security, logging, caching etc); it automatically handles singletons but allows you to easily "refresh" your application by recreating just one application scope variable (the factory reference); it allows you to transparently move CFCs around on the file system or swap implementations (you change the XML file only, not your code); it helps testability by isolating components from each other and abstracting dependencies...

    I could probably come up with many more benefits given a few more minutes of thought.

    If it's any consolation, I didn't "get" ColdSpring at first and I too thought it was overkill. I switched from Mach II to Model-Glue on one project initially to get better configuration data management through the built-in ChiliBeans factory in MG (because I did *not* want to use ColdSpring back then). Only later did I drink the ColdSpring koolaid and realize that I could have simply added that to my Mach II application to get the benefits that I initially switched to Model-Glue for...

  • # Posted By Patrick McElhaney | 11/2/06 7:16 PM

    Thanks Sean.

    I don't think I've ever run into a problem with circular dependencies. That may be because I fight hard to avoid them from the outset, or (more likely) I just haven't done anything that complicated. But my gut feeling is if I ever have to worry about circular dependencies -- no matter how complex the problem is -- I've already lost an important battle.

    AOP, well, yeah, I've heard it can come in handy if used discriminately, but I'm probably not even going to try messing with AOP until I have at least 10 years of OO under my belt.

    Couldn't I just "refresh" all my singletons by deleting and recreating them?

    Moving CFCs around: hmm, I'll have to ponder that one for a while.

    Helping testability: I fail to see how COLDSPRING does that. Loosely coupled components helps testability and maintainability and probably even helps avoid circular dependencies. ColdSpring just happens to require loosely coupled components (I guess). It doesn't decouple tightly coupled components (does it?)

    Now I have a whole new reason to be wary of CS. It seems like it does too much! I've lately become fond of the UNIX approach (a bunch of small pieces that each do one thing well) as opposed to the .NET approach (a gigantic framework that does everything badly).

    Ah, well, maybe I'll come around one of these days. :)

  • # Posted By Sean Corfield | 11/2/06 7:56 PM

    Circular dependencies are not bad in and of themselves. There is nothing wrong with two objects both holding references to each other (which is a circular dependency - A depends on B and B depends on A).

    AOP :)

    Refresh: Well, yes, but you'd need to be careful about thread safety if you have a number of separate entities in application scope that depend on each other (not shown in Brian's example but very likely in most real applications).

    Testability: If you let ColdSpring manage dependencies and object initialization, you can use different "wiring models" for testing, having ColdSpring auto-wiring mock objects into the model or applying AOP to stub out method calls. In other words, without changing your CFCs or their interactions, you can assemble them in different ways to test various scenarios (e.g., a test ColdSpring XML file can stub out the data access layer so you can test components with "canned" data). It's a powerful technique.

    ColdSpring actually does very few specific things - but it enables developers to do a lot of things a lot more easily.

  • # Posted By Patrick McElhaney | 11/3/06 8:31 AM

    Circular dependencies. Yes, you're right. I don't know what I was thinking. I guess I'm just used to reading the phrase "circular dependency" in reference to a problem that we want to fix (with a design pattern, for example). "Circular reference" sounds much more benign. :)

    If I need to instantiate to objects that aren't valid until both have a reference to each other, how hard is it to make a factory method that does just that?

    If I had a web of dependencies, with say, a couple of dozen objects and many circular references sometimes involving four or five objects, I can see how that would get hairy and ColdSpring would come in handy. Still, I hope I could avoid creating such a complex network of dependencies in the first place. It sounds like ColdSpring gives one permission to create a big mess.

    Testability: As I understand, there are two basic rules to make an object testable.

    1. It should have single responsibility, and delegate the details to other objects.

    2. It should not instantiate the objects to which it delegates. Instead, it should be given a reference to an existing object (what you guys like to call Inversion of Control).

    If you follow those to rules, an object should be easily testable. You set up the object with whatever dependencies it needs, substituting mocks for objects that do real work. And since the mocks themselves have no dependencies, it's really, really simple.

    Why do I need ColdSpring to manage the intentionally simple dependency structure of my unit test? It seems to me that ColdSpring is itself a dependency that makes the unit tests harder to run.

  • # Posted By Patrick McElhaney | 11/3/06 9:57 AM

    Oh, I missed "Refresh."

    Hmm, I guess that makes sense. I guess I can't blow out an object and recreate it while another thread holds a reference to it.

    But again, it seems like a smaller, more specialized framework might get the job done more efficiently. Maybe some kind of pool. You initialize all your entities, bundle them together under one entity, and add it to the Pool.

    Then at the beginning of the request, you get a copy of the entity from the Pool. Each simultaneous request has its own copy, so you don't have to worry about threading issues.

    When it's time to refresh, you recreate all your entities and pass them to the Pool. The Pool then drops all the old entities (when they're not in use) and starts filling up with copies of the new one.

    Actually, you could even let requests share the same copy of an entity (if threading is not an issue). Just having that proxy in front of the Application scope allows you to let new requests get new entities without affecting exist requests.

  • # Posted By Sean Corfield | 11/3/06 5:48 PM

    I'm done with this thread. Like I said, I didn't see all the benefits at first, even when people explained them (like you and I in this thread). Eventually, I "got" it. Mostly because I tried it and it just worked.

    Maybe instead of coming up with continual arguments against ColdSpring, you could actually try it and see for yourself?

  • # Posted By Brian | 11/3/06 10:17 PM

    Patrick, if you want to re-invent the wheel for a "smaller, more specialized framework", more power to you. Go for it! I think you're missing the point, but there surely is no universal solution for every problem. I'd say ColdSpring solves about 99% of the problems out there in a very elegant way, but there may well be that 1% of problems that you have run into and I have not.

  • # Posted By Peter Bell | 11/15/06 2:08 PM

    Just saw this post (better late than never!). Firstly Brian - great post - very cool!

    Patrick, couple of extra comments.

    Circular dependencies come up all the time. CompanyService needs UserService to call UserService.getEmployees() and UserService needs CompanyService to call CompanyService.getEmployersName(). If you have 20-30 domain objects I find a bunch of circular dependencies not to be uncommon, and as long as you use setter rather than constructor injection, CS will take care of this for you. FWIW, you obviously can't have circular dependencies that both use constructor injection because one HAS to be constructed first - I wrote a simple DI framework using pseudo-constructors (create all dependent objects, then init them all, then return the top level object), but if one init() needs to call a method on one of the dependent objects this doesn't work. Shame as it would have allowed for circular dependency resolution with "constructor" arguments, but I'm refactoring later this week to implement constructor and setter injection a la ColdSpring.

    Don't blow off AOP. It rocks and will allow you to much better handle separation of cross cutting concerns. Obvious possibilities are logging, authentication and caching, but there are lots of cross cutting concerns. Google AOP and I guarantee you'll find a use for in somewhere in the next few projects. You don't need CS to write your own basic AOP engine (you can do it fairly easily using mixins), but CS is already there.

    The remote bean proxy creation is cool if you need it.

    I personally did reinvent the wheel and called it LightWire (http://lightwire.riaforge.org - upgrading shortly to add constructor and setter injection, AOP and support for XML as well as programmatic config files). To me the reason was that I wanted to be able to inject dependencies into transients and CS can do that (singleton=false) but isn't really optimized for doing that as Dave and Chris take a much more service layer approach - it's mainly a matter of preference.

    The process of replicating the core of CS isn't crazy, but why bother unless you have a good reason? If you don't like programmatic config files, you could use LightWire which supports programmatic configs, but first read Dave Ross' post on the subject - very good points:

    http://www.d-ross.org/index.cfm?objectid=24CC1D9F-...

    Also, as you mentioned, one of the requirements for testing is that it shouldn't instantiate but should be given a reference to the objects it uses. How are you doing that? If you've written your own DI engine (as I did), more power to you, but why bother? CS already does that!

    Final benefit: the XML config files for CS are now well known in the community so if you want to share with other people in the community or other developers, they are more likely to understand CS than your proprietary DI engine - only break a convention when there is a compelling reason to do so and in CF, CS is becoming the default convention. I'm even refactoring LightWire to work with CS XML files (although only implementing a subset of the functionality - the subset I need for my specialized use case). Will also make it easier for me to refactor back to CS if it ever optimizes its injections into transients so I don't have to maintain additional code (even if it is only a few hundred lines) and can leverage whatever Dave and Chris think up next!

  • # Posted By Phillip senn | 6/8/07 2:15 PM

    Thanks Patrick for holding up the banner for those of us who don't get it, but are keeping an open mind. I dare say that if during a presentation you had people hold up signs that said "I don't get it" vs. "I get it", the presenter would be shocked at how elementary the discussion would become. "ColdSpring for dummies" anyone?
    While this example wis a good start, it doesn't show how you tell ColdSpring to read the XML file, or what the XML file's name is. If the answer to that is something along the lines of "@$%^*!! It's right there!" then it may be the case that I'm just not seeing what's right in front of me. But honestly, I think I could understand it written in just plain code rather than hearing all kinds of analogies.
    Maybe after I get the debugger working on cf8 I'll be able to step through the code, but I'm even having challenges with that.

  • # Posted By Brian | 6/8/07 3:07 PM

    Philip all of that is explained in the ColdSpring documentation, but if it helps:

    Create ColdSpring Service Factory      
    <cfset serviceDefinitionLocation = expandPath('ColdSpring.xml.cfm') />
    <cfset application.serviceFactory = createObject('component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
    <cfset application.serviceFactory.loadBeans(serviceDefinitionLocation) />

    And then to get a CFC from ColdSpring:
    <cfset myCFC = application.serviceFactory.getBean('cfcName') />

    I didn't include this in the article because it's explained in the documentation. I figured the part people would be confused about wasn't how you run ColdSpring but WHY you'd run ColdSpring.

  • # Posted By Dominic Watson | 11/17/07 7:41 AM

    Thanks for the post Brian, that is a much clearer explanation of ColdSpring than there is in the docs!

  • # Posted By James Holmes | 2/4/08 4:07 AM

    One note on the documentation: at no stage is the getBean() method mentioned in any of the docs except way down in the remote proxying bit. I'd expect it to at least turn up once in the getting started part along with the other bits about bean configuration and how to send those bean configs to ColdSpring. I had to go through the source code to work out how to get a bean out of the factory (yes, I could probably have checked one of the example apps and saved time, but I like reading source code :-)

    It also seems the API docs are broken at the moment.

  • # Posted By John | 6/11/08 10:32 PM

    ColdSpring is clearly inspired by "Spring" of the java world...

    http://springframework.org/documentation
    http://www.theserverside.com/tt/articles/article.t...

    There are oodles of advocacy pages for Spring which are directly related and might explain its value better.