Advice for converting an old app

I just started a new job, where there is a pretty large, and complex app.
The app is not MVC, and there is no standard method of coding. It has a melange of coding flavors, beginning from CF 4.5, with CFC’s thrown in for good measure. In addition, it makes heavy use of CustomTags.
Don’t get me wrong, several of those customtags are very powerful and complex.

The number of folders in the app is in the hundreds, and the number of templates is in the thousands.

I have been evangelizing ColdBox since the day I started.

Apparently 7 or 8 years ago the team had evaluated Fusebox, and then decided it couldn’t work for them. Even so, an MVC coding methodology was never adopted, and they kept developing on top of it in a procedural manner.

If anyone has advice then I really need it now.
If anyone from team Coldbox can help please message me, and I can go into further details.

Thanks…

cbfan,

sounds like my situation years ago. I first stopped supporting parts of the app that we really didn’t need. the app got considerably smaller. perhaps it will be the case for you too. then I stopped supporting parts of the app that were never used by the end user. Many departments hated me, but I ended up with far fewer lines of code to manage.

I haven’t done this with ColdBox but with other frameworks.

I’ve done two approaches:

  1. If you have a new project - do it in the new framework - ideally it would be small and not too complex.

  2. If you just have one huge app - find a section of it that you think you could easily convert. I inherited a big mess of Fusebox apps and I ended up re-writing one of them in FW/1 and slowing expanded it over time.

And if you don’t have management buy in - remember the “it’s easier to ask for forgiveness than permission”

Start small and ideally you can demonstrate that having something in a MVC organization is easier to work on than a plate of spaghetti :slight_smile:

Jim

Jim

That’s a common position to be in and there’s no easy answer sometimes. The best solution in my opinion is just to start converting stuff over as you go. It won’t become a fully OO, MVC app in the first pass, but the goal is to make it better every time you touch the code. One good thing about ColdBox is it can coexist easily with a legacy site to allow for an incremental transition. In Application.cfc in the onRequestStart you can modify it to only run coldbox when it’s hitting the root index.cfm file which means links like /legacyPage.cfm will skip ColdBox. With that in place, start taking your old .cfm files and organizing them in your views folder. You can implicitly call views without even creating the handler as shown here:
http://blog.coldbox.org/blog/tip-of-the-week-implicit-view-dispatch

From there you can start creating handlers behind the views and moving business logic into them and switch the views over until they’re using the rc scope instead of form and URL. I’d recommend breaking off small chunks of the site when you’re assigned to modify them. Switch them over to ColdBox and then update any links to/in them. You can also start using the standalone libraries like WireBox, LogBox, CacheBox, etc in your legacy app. As you switch your code over, you can transition to the *Box family that’s baked in quite easily.

Your company isn’t likely to approve a 6-month project just to rewrite the site from scratch, and honestly that would be a much more risky and bug-filled process. Refactoring slowly takes longer but it’s more stable and successful. (http://www.joelonsoftware.com/articles/fog0000000069.html) Also, WRITE UNIT TESTS as you go. The more of those you have, the more brazen you can be in making sweeping changes and feeling comfortable that you haven’t broken anything. Good luck!

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Thanks to everyone who responded.

@Brad thanks for responding and your pointers. However I think both the legacy app and Coldbox use index.cfm… so what then? :frowning:

This is what I ended up doing…

The hierarchy is somewhat like this

RedesignedSite
– config
– coldbox.cfc etc
– handlers
– includes
– layouts
– views
Application.cfc - legacy application file, extends Coldbox and add the coldbox app mappings, etc. The legacy onRequestStart was tweaked to check the script_name, and then processColdBoxRequests()

If I move the login part of the legacy app to coldbox, and the menu/nav and startpage wrapper into coldbox, would I still be able to have something like the legacyPage[s].cfm get routed through the new login that’s in coldbox? and use the layout that’s in there?

ColdBox doesn’t use index.cfm, what id does is needs the index.cfm to process the CB request.

Brad or Luis can confirm this, but you could change ColdBox to use something else rather than index.cfm. For example in the Application.cfc is this

//Process a ColdBox request only
if( findNoCase(‘index.cfm’,listLast(arguments.targetPage,"/")) ){
application.cbBootStrap.processColdBoxRequest();
}

I think just changing that would enable you to run ColdBox on another template, like

mydomain/coldbox.cfm/route/handler/action/

Just create an empty index.fm or rename the one to what you would like. I think the framework has only one reference to index.cfm and that is the routes.cfm, but Luis or Brad could confirm that.

@cfban

I’m a bit late to the party admittedly but…

One thing you could try would be to have a method in your applicaiton cfc file that asses the URl based on the contents of the handlers directory.

E.g. write out the contents of the directory to an array and then check if there is a matching event within the URL?

If you have a match in the URL then could processColdBoxRequest(), otherwise just continue as normal?

We’re currently running asimilar kind of setup because we have some monolithic legacy beast which is a combination of legacy code, MAch-ii code, and Codlbox code…and we have to do things this way to try differentiate betwene mach-ii and coldbox event calls…

Now then, this is a bit simpler for us to detect since we have a closed backend applicaiton and are not using SES urls, so may not be applicable for your scenario, but could be worth considering if your really stuck?

Example code (from application.cfc):

<cfif !structKeyExists(application, ‘cbEvents’) || structKeyExists(url, ‘reloadCbEvents’)>

<cfif structKeyExists(url, ‘event’) && url.event neq ‘’>

<cfif ArrayFind(arrCBEvents, eventCalledDotDelimeter) gt 0 || ArrayFind(arrCBEvents, eventCalledColonDelimeter) gt 0 || ArrayFind(arrCBEvents, eventCalled) gt 0>

<cfset reloadChecks() />
<cfset processColdBoxRequest() />


Its probably a bit convoluted and could be handled a bit better (so go easy on me guys!!!) but this should give you the general gist behind it?

Hopefully might be of some help :slight_smile:

Regards,
Will

Thanks everyone for the advice. I’ve used bits and pieces to get things working for now.

I don’t know if this is the best solution, but we are currently using an IIS Rewrite rule, to actually do a redirect of the legacy URL’s. So for example http://myApp/products/listAll.cfm, goes through an IIS rewrite rule to become http://myApp/index.cfm/products/listAll. This then gets processed as a CB request, and then the implicit view handling, finds the legacy files.

My next question is. So this app, has an internal and external app. Our business is a SaaS app, and we have a standard vanilla version. But then our customers have their own customized version. However, as far as the application goes it’s one app. Now that I’m starting a very slow process to convert everything to Coldbox, I’m looking for some advice as how to go about this.

As far as the software goes it’s one app, one entry point, with global core code, and our clients would use the core codebase, and basically only extend whichever parts they wanted to customize or change.

What’s the best way to go about that?
In my handlers directory, should I have subfolders per client? What about views?

Thanks,
cbfan

I wrote this blog Article that may help, but the concept it followed was the same.

http://www.andyscott.id.au/blog/writing-a-coldbox-application-to-handle-more-than-one-site-with-orm-support

First you need to work out how to decide how the application is going to identify the client / customer additions, but what I did was write an interceptor that would change each request on handlers / views / models etc. Using the External Locations I think it was.

The way it was laid out, was something like

handlers
-Company1
-Company2
Views
-Company1
-Company2
Entities
-Company1
-Company2

If a component could not be found / handler or view, it would default back to the parent views etc. Global code would be placed into the root and the current request would point to either Company1 or Company2 extend the component in the root.

The trickery was getting a component to load if it was not found from the root rather than the companies folder.

Hope that helps, but at least it may help get you going.

Andrew,

Thanks, yeah that’s sort of what I have in mind.

Just a quick question though, I want the app, to first check if there is a extended CFC first, and use that if it finds it. Then if it doesn’t, to fall back to the parent cfc (be it handler/view, whathaveyou).
Do you have similar rules or behavior in your setup?

Thanks,
cbfan

Yeah that was the trick that I had problems with, I will see if I can dig up any code that I have but it would have been written in the ColdBox 1.5 days.

In todays version of ColdBox, it could be as simple as DI’ing the correct component.

I can’t find the blog post at the moment, but there is a post where I use WireBox to replace instantiated objects based on certain requirements. So anytime say Company1 is accessed you could then swap it to the parent if the child doesn’t exist.

But basically this is the code I use to swap out things in ContentBox.

//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
public void function afterInstanceCreation(event, interceptData) {

if(interceptData.mapping.getName() == “AdminMenuService@cb”) {
interceptData.target = getModel(“adminMenuService@cronus”);

}

}

Now, if you are not using Wirebox this could make it a lot more problematic, but is achievable.

Andrew,

Thanks again, you have been a great source of information.
What you describe, is what I have been discussing with my colleagues, that, we would put some logic in the wirebox config or something, to do some checking.
For the legacy code, we’ll leave it as is, there’s some legacy code that does the detection, and maps the object paths. So that can remain for the legacy CFC’s.
But for the new code, I would use wirebox. Just wondering though, about the overhead of having voluminous procedural CFC’s, for the legacy code, plus wirebox, for the new ones.

Good to know we were on the right track.

thanks!
cbfan

Not sure to be honest with your last question, the wirebox is a small overhead but it gets cached so there is no issues long run. Never done it with legacy ColdFusion apps, so can’t really say for sure.

How many customers will you have? Are we talking about 5 to 10 customers who will each be rolled out in a specifical release with custom code written just for them, or potentially millions of customer who need to sign up online and have their custom site working in minutes with no human interaction, but certain features enabled/disabled?

In the past when working with a small number of custom sites, I’ll actually create multiple web roots and IIS sites and put all models, handlers, views, etc in a core folder that each site pulls from via external locations or inheritance. Since local conventions are checked prior to external locations, placing a handler or view of the same name in the local site will “override” the external one. Here’s a connection recording that shows some tricks you can do:
http://experts.adobeconnect.com/pzu9rc9pc0/

If you’re talking about thousands of sites (or even dozens), I would recommend basing it all out of the same web root and use domain name, or logged in user to determine functionality differences. How you organize the code is pretty much up to you, but I would agree with folders for each company. For simple differences, you can create flags or settings to toggle features. For bigger differences you can use inheritance and extend base services/handlers/models and override base method in the super class to change how things work. Then create logic to load the appropriate CFC for the logged in user.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com