I've been playing with FW/1 a bit on a personal app, and it has proven incredibly frustrating due to multiple manifestations of a single problem: your Application.cfc HAS to extend the framework in order to use the framework. My complaint really has nothing to do with FW/1 in particular, the exact same argument could be made against Fusebox's Application.cfc integration (but FB at least provides a "normal" way to use it). And just to be clear, I'm also not railing against Sean Corfield, even though he happens to be the author of both FW/1 and Fusebox's Application.cfc integration.
The first problem is due to Adobe's seemingly mindless choice to require the use of Application.cfc for per-request settings (datasource, mappings, ORM config, etc.), rather than doing it the right way with tags (in the style of the CFAPPLICATION tag). With Application.cfc being the only place you can define any of this, you cannot use Application.cfc for an individual frontend, since it has to be shared across ALL frontends.
Consider a prototypical blog. It has a public side (where the public reads), and an admin side (where authors write). Two separate front ends; one single application. If your front controller is bound to Application.cfc, you're forced to either run two separate applications or a single dual-purpose frontend. Either one is a mess, either reducing encapsulation or increasing duplication. At the very least.
Now consider a different example: an app with a single frontend that also needs one, single, solitary, standalone page for something. Maybe even just for a quick one-off test script. You create 'test.cfm' in your directory (so it gets the proper Application.cfc context so you can do your ORM magic), and hit it in the browser. Oops, your framework decided with it's onRequest handling that it's going to just do it's thing, completely ignoring your template. Different manifestation, same problem, though this one can be mostly addressed by overriding onRequest with custom behaviour that conditionally invokes super.onRequest.
Like the majority of places where inheritance is used, the proper solution is composition. Rather than having your Application.cfc extend the framework, let your application compose the framework into itself. That way it happens on the application's terms, rather than the framework's terms. I understand that just extending the framework is desirable for ease of initial setup, so I'm not saying you can't do that, just you (as a framework author) should provide an alternative (like Fusebox's fusebox5.cfm). Then I (as the application developer) can decide how the framework should be used.
Just to be clear, the problem with using inheritance where composition is the correct choice rests on both the shoulders of Adobe (for making request settings part of Application.cfc, rather than composed in with tags), and framework authors (for requiring Application.cfc to extend the framework). Addressing either of these problems would handle the first manifestation (paragraph 3), but only the framework authors can deal with the second manifestation (paragraph 4).
Bottom line, don't wire yourself in so you can self-invoke, let me invoke you when and where I want you.
I'm a bit confused. Well, I'm a lot confused. But I want to focus on one thing. You repeatedly make the argument that Adobe made a mistake by saying request level settings had to be made in App.cfc, and that they should be made in tags instead. Can you explain to me what you mean by that? If I use App.cfc to say that application.bgcolor=red, how would I do something like that in a tag based form? Or I guess I'm saying – can you give a concrete real example of how files would make use of global settings w/o one place to set them?
@Raymond
Not application variables, request settings: the attributes of CFAPPLICATION, plus per request mappings (added in CF8?), and now CF9's datasource and ORM config. They HAVE to be expressed in Application.cfc, there is no other way to do it, which forces you to use Application.cfc in a very specific way if you have a multiheaded application.
A much more flexible approach would have been to create a addMapping(map, path) function (instead of Application.cfc's this.mappings struct), and have ormReload accept an optional config struct (instead of Application.cfc's this.ormsettings struct), so that you could configure them in a Application.cfX-neutral way. In many (most?) cases the addMapping calls would still be within Application.cfc's psuedoconstructor, and the ormReload call would be within onApplicationStart.
And it's worth repeating what I alluded to, but didn't explicitly call out: these are PER REQUEST settings. They are recreated from scratch on every request. They have nothing to do with the application scope (as evidenced by the fact that the can easily change without any application event firing or any application-scope data access). The ORM stuff is persistent (only read on requests when ormReload() is called), but it's still not a application construct – you can happily redo your config and call ormReload at any point, without affecting any aspect of the application scope or firing any application events. For that matter, once you've set it up, you can remove the settings struct completely without issue (though you do need to leave this.ormenabled in place).
Because of this incredibly tight binding between these settings and Application.cfc, having a front controller framework come along and express equally tight bindings (but in an orthogonal direction) becomes very problematic. Adobe has pretty much mandated Application.cfc-to-business-layer coupling, so a front controller framework mandating a Application.cfc-to-presentation-layer coupling only works in the case where you have a one-to-one relationship between business layers and persistence layers. For simple applications (i.e. evangelist demos) that works, but my experience has demonstrated that that's the less common scenario in the real world.
With CF7/8, it wasn't such a big deal because it was only your mappings and CFAPPLICATION settings that were coupled by Adobe, but with CF9, your ORM is similarly coupled, which is a BIG problem. Mappings and cookie settings are usually pretty stable, so duplicating them isn't a huge deal, but you can't duplicate your SessionFactory without losing the ability to use the L2 cache as well as creating some interesting development problems around reloading and such.
Regarding application variables and their initialization, you absolutely want them in one place, but you don't HAVE to do it in Application.cfc's psuedoconstructor. You have the freedom to set them up wherever you want, as long as they're in-scope before your code needs them. That's the piece that is missing with both Adobe's choice of implementation for the request settings and the offending front controllers' integration point.
Bottom line, dealing with ONE of the inheritance couplings is ok, because you can do single inheritance with CF. But when you have to deal with both, you're screwed.
I thought I'd jump on, since LightFront framework development is a bit quiet these days, but it is being actively used. Since FW/1, FB3Lite and LightFront are all designed to be "light" frameworks, I thought I'd point this fact out.
Although it's LightFront's default for the Application.cfc to extend lightfront.cfc, and the examples currently show it that way, there's nothing stopping you from developing a LightFront application where Application.cfc DOES NOT extend lightfront.cfc. It's not documented, yet, but it absolutely does work if you need it to be configured that way.
It also does not need to process every request. In fact, an application I'm building these days for a client does not use LightFront throughout the entire application, only the pages I tell it to. LightFront can also be used in the pages that do not call the framework directly. It's pretty flexible that way. In fact, it's more flexible in that regard than either FW/1 or FB3Lite.
I have a lot of documentation left to write for LightFront, and I will get to it soon, but I'm too busy these days! :-)
Could you just put your unrelated test code in it's own folder with it's own application.cfc and inherit and override parts if you need to?
Multiple inheritence could be a problem but I doubt many application frameworks would place nicely together if you tried to stick them into the same space anyway…
Or perhaps I'm still missing your point as to what can't be worked around :)
I'm afraid I still don't quite get it Barney. You would want a function to specify ormSettings, but you wouldn't want to use it in App.cfc. So I'm not sure _where_ you would use it, and if you didn't use it in one particular file, wouldn't you run the risk of having 2-N copies of the same settings that would need to be updated?
It almost sounds like you are saying that because of App.cfc, you can only build evangelist demos, which I'm sure you really don't mean. There are probably millions of apps out there that go beyond simple demos.
Maybe I'm coding "wrong" – but I've never run into a problem with the use of Application.cfc. I've never felt like I couldn't do what I need to do.
100% agree with Barney here. By imposing that per-request settings be specified in the Application.cfc pseudoconstructor, the SRP design principle is violated. Application.cfc now has too many possible reasons for change.
Also, I agree with Barney that developers should have as much choice as possible, as to when and how to invoke frameworks. Forcing developers to extend a framework Application.cfc makes this unneccesarily difficult. Frameworks should not dictate how we write our apps, they should just do work for us when requested. Stick to the http://en.wikipedia.org/wiki/Hollywood_Principle ! ;)
I love your blog and what you write, but the prototypical blog example isn't showing a problem that can't be solved by using different view and controllers. For example the public sees '/views/blog/post' and the admins would see '/views/blogadmin/editpost'. No code duplication and its 100% DRY. For one off scripts just make another view dir and view. All my projects usually have a '/views/scribble/default/' for development.
Inheritance vs composition for Application.cfc… not really getting the point. Since Application.cfc is the FIRST thing that happens on every request how would composition for Application.cfc work?
Interesting post and your blog rocks.
@John "Since Application.cfc is the FIRST thing that happens on every request how would composition for Application.cfc work?"
I think that is exactly the point. Because you are forced to put per-request settings and in Application.cfc you cannot use composition to handle those settings more flexibly. Instead you're stuck with inheritance if you want to overrule something. If you could handle per-request settings outside Application.cfc, this would allow for greater flexibility, as well as adjusting settings at runtime.
@Martin – So taking one setting, like mappings – how would you do it if you didn't have App.cfc? I mean given that we know it runs first, it is an excellent way to define pre-request settings, but it sounds like you are saying this is a bad thing. I'm getting really confused here. Can you provide some pseudo-code perhaps – ie your ideal world scenario? Again, we can focus on one thing, like
cfset this.mappings["/root"] = "this path"
That's a common mapping I use now.
@Ray I am not saying we should not have App.cfc, just that I would prefer the possibilty of defining per-request setttings where ever I choose.
Given this App.cfc
If you could specify mappings with a tag, you could have your mappings specified in a seperate CFC, maybe like this:
I personally would prefer this possibility for two reasons:
1) I allows for great flexibility: Suppose my app detects that a shared network drive is broken, and I want to redirect all uploads to a failover network drive so that my app can continue to run while I fix the problem. All I would have to do is this:
But since we are stuck with App.cfc, I would have to edit the App.cfc mappings code manually on a running production server. Yuck! Alternatively I could check for availability of the network drive in the App.cfc pseudo constructor and specify mappings conditionally but I think you will agree that it would not be a very elegant or maintainable solution.
2) In my opionion, mappings config need not be parsed on every request. In general, mappings do rarely change during the lifetime of an application. In the above scenario the mappings would be cached, only to be refreshed when application.mappingsConfig.init() is called. It might not make a very large difference, but I know little things like this can add up (I work on an application that sees up to 7.5 million daily page views). If you want the settings refreshed per request, you can put application.mappingsConfig.init() in onRequestStart(). Again this choice we do not have since we are stuck with the App.cfc pseudo-constructor.
One more thing to note is that in Railo, you can specify per-webcontext settings (including mappings) using the tag.
Hmmm… Seems the code has been stripped out. Hold on a sec.
Application.cfc: http://pastebin.com/f44bb4038
MyMappingsConfig.cfc: http://pastebin.com/f3107ed1
Code for switching to different set up mappings at runtime: http://pastebin.com/d7d005b8f
Subscribing…
Martin, thanks for the code samples. It is a lot clearer to me now. It seemed to me as if Barney was saying the entire concept of App.cfc was broken. I may have misunderstood him. I can see how what you propose would give additional flexibility, although you have to admit it is a good bit more complex. But I can agree with the idea that mappings should not need to be defined on every request.
Hi Ray,
Glad the code samples provided more clarity. As for my understanding Barney correctly, only he can tell. :)
Regarding the increased complexity: this is definitely true for my example. On the other hand if we DID have a tag that could be used anywhere, I suppose you could still put it in the App.cfc pseudoconstructor if you like to keep things simple.
I like the idea, Martin. A cfmapping tag would allow for added flexibility. Has this been submitted to Adobe as an enhancement request?
Open BlueDragon has a cfmapping tag since version 6.2.1 (see http://wiki.openbluedragon.org/wiki/index.php/CFMAPPING and http://www.creative-restraint.co.uk/blog/post.cfm/per-application-mappings) Railo has a complete and seperate administrator and cfadmin tag for every web-context (including mappings) and ColdFusion has this.mappings in the App.cfc constructor (Railo has this as well, dunno about OpenBD)
So we have three different implementations, of which the paid engine has only the sloppiest one. Starting to sound familiar, Adobe? ;)
Subscribing too…
just listening in…
I think we are forgetting one *extremely* crucial point here and that is that the Application.cfc IS really a framework unto itself; it's the ColdFusion Framework. So yes, when you choose to use the ColdFusion Framework (you can quite easily choose NOT to use it and still build applications), there is some coupling that your application does have to make to it.
I don't see this as a problem, and, in fact, it has made building applications much easier, at least in my experience. If anything, I with there was *more* stuff I could define in the App.cfc, such as per-application JAR paths for a app-specific class loader.
Also, I am confused on how you would go about creating centralized settings withOUT a centralized point of entry? Or, are you staying that we should be creating explicit front-controllers with index.cfm (and apply all the per-request settings there via tags + methods)?
@Martijn van der Woud – thanks for the clarification.
@Ben – Exactly. Application.cfc is a framework. This is a crucial point. A super simple front controller like FW/1 really just adds some more expected behavior for HTML requests.
@Ben, @John – good point – application.cfc is definitely a base framework, but I think it's also not unreasonable to ask for more flexibility in the way it's used, especially when considering the case Barney/Martin describe (ORM, multi-headed, etc.)
Ben's point is important: when Application.cfc was introduced, it was as a front controller. It has onRequest() to intercept every request – the very definition of Front Controller! Application.cfc IS a framework.
If you don't like that, don't use it: stick with Application.cfm!
Personally, I like the way Application.cfc works. I don't have any problems with its per-request nature. Application.cfm was per-request too, so I don't really get what you're complaining about anyway…
I have to echo Sean here. While I saw an interesting ideas here (re: setting up mappings differently), but I've yet to see any proof/reason why App.cfc is "broken".
I never stated that App.cfc was 'broken'. And I do not think that Barney was trying to assert that either.
Two points that are important as I see it:
1) It would (sometimes) be practical if per-request settings could be defined anywhere, instead of being restricted to the App.cfc pseudoconstructor
2) Framework authors should provide their users with as much flexibility as possible, regarding the way their framework is invoked. If two frameworks force me to extend their base App.cfc, then it is impossible for me to use both frameworks together. It is all a matter of perspective I guess: is the framework running my application? Or is my application using the framework? I personally personally prefer the latter.
@Sean
"Personally, I like the way Application.cfc works. I don't have any problems with its per-request nature. Application.cfm was per-request too, so I don't really get what you're complaining about anyway… "
It is not the per-request nature of Application.cfc I have a problem with, but the restriction that per-request settings be defined there and nowhere else.
Also, we are trying to exchange technical opinions here, so that we all may learn from each other. If we don't agree with each other, that's for the best. If we all agreed nobody could learn from this discussion. I must say that I find your labeling this as "complaining" a bit demeaning. Please keep it positive!
Ok, not broken, but "evil". Come on – you have to admit thats a strong term. ;)
1) I concur.
2) I guess I just don't see the concern in this – I'd never use FW/1 and Model-Glue in the same app.
As for being positive and not complaining, again, I'll point out Barney's words here – "evil", and "The first problem is due to Adobe's seemingly mindless choice". Those are strong statements and not very constructive I think.
@Raymond you are right about "evil" – and "mindless choice". Compared to that, Sean was being polite. Point taken.
@Martijn, you know you can still use the cfapplication tag with Application.cfc-powered apps? And on Railo, you can use cfapplication to modify existing settings (as well as simply overwrite them) and I think you can do everything via the tag that you can set via Application.cfc (mappings, custom tag paths, default datasource etc).
Like Ray, I can't imagine trying to use multiple MVC frameworks in a single app so that's a straw man argument.
As for "Framework authors should provide their users with as much flexibility as possible, regarding the way their framework is invoked." – absolutely not true. The framework runs the application by definition. Ruby on Rails is successful because it is opinionated software. cfWheels, ColdBox, Fusebox, FW/1, Mach-II, Model-Glue all dictate how your application runs. They all "force" you to do certain things in particular ways.
You mentioned the "Hollywood Principle" earlier but you got it the wrong way round: the frameworks all follow that principle in that you do not "call" the framework, it calls you. You write CFCs and .cfm pages and the *framework* calls methods on those CFCs and includes those pages.
Now, it's perfectly fair comment if you don't like that approach but I don't think there are any (application) frameworks in the CFML world where your code acts as the controller and invokes the framework…
@Sean, it is good to know that we can still use the cfapplication tag in App.cfc-powered apps. I did not know that. But I guess there are still many things you cannot do with this tag, like mappings and ORM settings. With the cfadmin tag on Railo (I mentioned that one earlier) you can indeed do anything you can do in the App.cfc pseudo-constructor. Actually I believe that Railo's admin GUI is using this tag internally for managing datasources, mappings, archives, cache settings and all the other Railo awesomeness.
Railo is a good example of providing the flexibility and choice that I personally like so much. For instance I have four ways to define a datasource:
- this.datasource in App.cfc (http://www.railo.ch/blog/index.cfm/2010/2/19/Random-Friday-Railo-tip-thisdatasource) – datasource is Application-specific
- the web administrator GUI – datasource is web-context specific
- the server administrator GUI – datasource is server-wide
- the cfadmin tag – datasource is either web-context specific or server-wide, depending on tag attributes
That said, I think I see your point of frameworks being successful because they do NOT provide flexibility and are built from the perspective of running an app instead of being run by it. I do not particularly like it from a design standpoint. But I guess it makes working together more easy if everyone is forced to do things the same way. And honestly that might be more important than losing some flexibility. And maybe most people find it comfortable to be forced to do things in some standard way. It's just one more thing you don't have to think about, which is a legitimate line of thought of course. I guess I am not like most people ;-)
As for your comment that "cfWheels, ColdBox, Fusebox, FW/1, Mach-II, Model-Glue all dictate how your application runs." True. But some frameworks are more restrictive than others. For example, ColdBox forces my App.cfc to extend their coldbox.system.coldbox cfc. And the framework is loaded in the onApplicationStart event handler. ModelGlue does not force my App.cfc to extend anything, and is loaded by a cfinclude in index.cfm Personally I think the latter is more elegant and allows for greater flexibility, like having .cfm templates in the same app that do NOT use the framework.
Purists begone! Sean, really, it's not a straw man argument. Sometimes you have barely enough time to get the job done, nevermind rewrite all that accumulated spaghetti or consolidate existing framework based code. The lesson: don't inherit from multiple frameworks and drive.
Regarding "Application.cfc IS a front controller", I'll agree, but it is also a persistence manager and an event listener. Adobe's choice to force you to use a single construct for all of these things puts the developer in a rather tight spot. I much prefer Application.cfm to Application.cfc for exactly this reason, but unfortunately you cannot use CF mappings, default datasource, or ORM configuration unless you use Application.cfc.
And Ray, yes, it is possible that "evil" is too strong a word. However, I literally don't have a single multi-template application (personal or at work) where FW/1's requirement of having my Application.cfc extend the framework isn't a dealbreaker. This is undoubtedly due in part to personal style, but the fact remains: the front controller is NOT the application. If we could define the application-y stuff outside Application.cfc, then Application.cfc being hijacked by a front controller is no big deal (and would be desirable). But as it stands today, you don't have that choice.
@Barney,
You took the words right out of my mouth man. I agree with every single point you've made as its totally in line with the golden rule to always favor composition over inheritance. Excellent post man!
When you have a chance, we're working on a project called cfcommons, created by Brian Carr. There will be a Part 2 follow up from Part 1 in which we will implement SimpleMVC to build it into a proper web application where we use composition in Application.cfc instead of inheritance.
We'd love your thoughts, thanks man!
So, I have a question:
If you have a front controller which in order:
1) Fires application.cfc (by way of being a CF page) which defineds app data/settings)
2) Defines a request.context object (session, security login, other global per request stuff)
3) Cfincludes the application page (cfm controller file)
and
4) Each controller gets it's page specific stuff (form data. etc.) by instantiating an object specific to that page, an object which extends a base class (object).
Then, either the page object or the base class (object) can redefine any application settings it needs to, on a per request basis. The application only defines default mappings, data sources, etc., but the page object never reads the application scope directly. Rather, a page object (or controller cfm if controllers aren't object based) would always read app settings from the context object, which gets created at every request by the front controller. The context object overrides settings created in application.cfc as the situation (the context) requires.
Or this kind of arrangement not the way to go?