Options for deploying CFCs on an Application

I've had several conversations with people on various mailing lists about what is the best way to structure your CFCs so that they:

  1. can be typed via 'return' attributes of cffuncion of 'type' attributes of cfargument
  2. can be set in 'extends' attributes of cfcomponent
  3. are very portable – i.e. it is easy to use com.io.FileReader in one project, and you can pick it up and move it to another without having to do a global find and replace to change com.io to com.domain.io.
  4. are able to be used in different applications across a domain – i.e. if you have one application at /myapp1/ and another at /myapp2/ they can both have a com.myCFC that can be named the same, but do differnet things, and they won't intefere with one another.
  5. are easy to maintain.

Unfortunately, I don't believe that there is currently a BEST option out there to cater to all of these things, but I'm going to go through all the options I have found, and describe the pros and cons of each, and you can make your own decisions (For those of you who are on CFCDev, I'll be taking a lot of stuff out of what I wrote there).

Use a CF Mapping
In a lot of cases this can end up being the best option.  It solves all your issues with typing (1, 2), however it does not make the code very portable, in that you may not ever have access to the cfadmin to create the mapping, and/or if the mapping needs to change, you have to edit your code to work with that, so you cannot simply just drag and drop. 

To work around that problem, you could either (a) use the ServiceFactory to check if the mapping already exists, and if not create it in the appropriate place and/or (b) create a mapping that looks like "/com/mynamespace/mystuff/" that will nicely translate into a path like 'com.mynamespace.mystuff'.  The advantages of (a) is that you can make a entry point to your CFC lib (most likely a Factory) setup the mapping as necessary, and therefore you don't need to go to the cfadmin, however, obviously it is a undocumented feature.  The advantages of (b) is that you are almost totally guaranteed that someone hasn't already used that namespace (much like packaged in Java).

The cons of cfmapping (and the ServiceFactory workaround above) have to do with code portability and using the same CFCs across a domain (3,4).  Since a mapping is server wide, if the applications have differing code, it's going to get very messy. You will end up having to set a unique mapping for that application, and therefore break all your return, extends and type attributes in your CFCs.

This would work very well for application specific code, but not code that you wanted to port from one place to another, such as a product or framework (I shudder at the thought of having different versions of framework on the same server that used a mapping…).

 

 

Put all your CFCs in one directory
Placing all your CFCs under a single directory solves most of the above problems, particularly the one with applications spread across a domain (4) and since all the CFCs can see each other typing is not a concern.  The major issues here (I feel it's major) has to do with what happens when you start looking at systems with large amounts of CFCs.  Having anywhere above 20 CFCs in one directory is going to start to get very hard to work with, and could cause some maintenance frustrations in the long run. 

One of the nice things about packages is that they can be relatively self documenting, assuming they are named well, and can help break down an application into manageable 'chunks'.

This would be useful if you have a small amount of CFCs and/or having multiple CFCs across domains with the same name, but different functionality (4) is of utmost importance.

Use relative '/' rather than '.'
I can remember when I first found out about this technique – I thought it was going to be the absolute best thing ever.  However, like all the rest it has its drawbacks.  It handles issues of typing quite well (1,2), but does mean that you can end up with some very odd package design, and more often than not, most of your main CFCs in the same directory anyway. It is very portable (3), but only within a given OS, as '/' must be used on *nix, and '' must be used on Windows, which pretty much defeats the purpose of portability.  Otherwise you have no problems with multiple apps across a domain (4) as all the mappings are relative.

This would be most useful when you know you aren't going to change OS, and being able to use code across a domain is very important.  This couldn't be used for product development simply because of the OS issue (and it's an undocumented feature).

Don't use typing
This essentially boils down to not using inheritance (because you have to hard code it), and all type and return values are either 'any' or 'web-inf.cftags.component'.  One could argue that since CF won't check things until runtime anyway, this won't make any in terms of how errors are fired on the system. 

To help bring back some sort of typing (1,2) you could do some sort of testing to ensure that a CFC is the CFC you require (via getMetaData), or that it has the given method you wish to call.  This can cause further overhead, especially considering that getMetaData is known to be pretty expensive.  It will also take a little bit longer to develop as you have to write the extra code.

However – this code is very portable (3), and can be used across domains easily (4), but you do lose a lot of the self documentation that typing gives you, and could leave you wondering which CFC you were meant to pass in as an argument.

If you were writing a product to be used it a variety of places, this may be an appropriate place to use this method, but for application specific code, it is probably overkill.

Store them in a directory in the root of your domain
This seems to be the most 'rounded' of solutions for placing your CFCs.  If you notice, Mach-ii, model-glue, Tartan etc all require you to include their install files in a directory in the root of your domain, and since we figure the people who wrote this stuff are a *little* bit clever ;o) so we can assume that there is a reason for this.

This solves issues with typing and portability (1,2,3) and is obviously easy to maintain (4).  The issue comes from where you have multiple applications running across a domain (quite possibly a relatively low percentage of apps?) with different versions of CFCs required on them.

If application /app1/ is running v0.8 of their framework, and /app2/ is using v0.9 and they BOTH have to have their CFCs in the root of the domain – you are either going to run into a mess, or you will need to test app1 all over again to make sure nothing breaks with the new version.

This seems to be the best option for when you are developing a product (particularly frameworks) as it is the most portable version, and it is very unlikely you will not have access to the root of the domain.  If you use this for application specific code is probably dependent on what is being developed on that server.

Of course there is nothing to stop you from chopping and changing different aspects of these approaches into whatever happens to work for you.

I think I managed to cover all the approaches that are possible – but if you have some other way, please let me (us?) know.

Leave a Comment

Comments

  • Jim | September 15, 2005

    Re: Dont Use Typing
    "This essentially boils down to not using inheritance"

    I completely disagree, in fact it boils down to only inheriting from within the same folder, which forces you to package your app cleverly, rather than being able to inherit from anywhere….

    "you do lose a lot of the self documentation that typing gives you, and could leave you wondering which CFC you were meant to pass in as an argument"

    True, but use of the hint attribute could provide much more information in that regard than simple typing

    Re: Root of your domain
    "The issue comes from where you have multiple applications running across a domain (quite possibly a relatively low percentage of apps?) with different versions of CFCs required on them"

    Regarding percentages, I guess globally thats hard to assess, but in my experience of selling hosted apps it is 100%!

    "you are either going to run into a mess"

    And the mess is huge – either use seperate instances of Jrun, which we have not found performant, or buy another machine…

    "This seems to be the best option for when you are developing a product (particularly frameworks) as it is the most portable version"

    Portable, but conflicts with itself! What kind of portability is that? It does have value with frameworks, but the generalisation of "a product" is very wide….

    So i guess my vote goes to not using typing!

  • Mark | September 16, 2005

    Jim,

    ‘…in fact it boils down to only inheriting from within the same folder…’

    I would have looked at using the Inheritence only within a directory a combination of ‘Put all your CFCs in one directory’ and ‘Don’t use typing’. Hence my comment at the end of the post about being able to ‘chop and change’ different approach. But there is definatley nothing wrong with this approach.

    ‘…but in my experience of selling hosted apps it is 100%!…’

    Well, my experience is more with applications on a corp intranet – so it’s FAR less than 100%. So obviously it depends.

    ‘..Portable, but conflicts with itself!..’

    Well – my theory here was – if you are developing a ‘product’ as such, it would be most likely to reside in it’s own domain – so it’s very unlikely to have to deal with issues of crossed versions within a domain.

    My whole point was to basically say that there is no hard and fast rule. Depending on what you are doing, what you are trying to accomplish, and what comprimises you are willing to take, will determine how you deploy your CFCs.

    I personally hate not being able to type CFCs, so I shy away from not typing things, but that is probably more because of me working with typed languages and liking it – someone else on the other hand may have no issue. So it’s all relative.

  • Dale Fraser | July 10, 2007

    We use the mapping method.

    And in CF8 you can create the mapping at the application level. So this is the perfect solution.