5

How can you manage something like that? I tried my best to design the subsystem to be reusable, but there are just certain things that are unique to the site that have to be customized (e.g. fields in Account entity, or cfc="" in orm annotation).

I thought of using SVN and branch out the customization, but we found it very cumbersome quick as the commit person has to decide an enhancement / bug fix belongs to the Truck or Branch every time and once something is missed, is not easy to undo.

So... What's the best way to handle this situation? Just clone the set of code into a new source control, and fix bugs on both/all source controls?

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
Henry
  • 32,689
  • 19
  • 120
  • 221

2 Answers2

3

Assuming you're not using some sort of MVC framework... yet...

I've used a couple of different methods for handling these sorts of situations.

Depending on what seems to be different and the same between CFC's, you can:

1) use inheritance

If the vast majority of your code is the same, but each project has just a few differences, then refactor the common code into a base CFC which you extend for each project. This allows you to maintain a single set of tests for most of the functionality, then just add further tests for code that deviates for each project.

The drawback to this is that it requires some planning and discipline to maintain forward compatibility when modifying the base classes. You are, in essence, defining an API, and doing that well is not an easy task.

2) use mix-ins

If what you seem to be duplicating each time is functionality/logic, then try refactoring that logic into chunks of code which you cfinclude into any cfc's that require it. See Ben's experiments with mixins and Sean's comments on mixins.

Sadly, the mechanisms for doing this well in CF are weak, unlike Ruby's modules.

If your needs are as you describe in your comment, "something like, the logic/business rules are the same, but the input and data collected are not the same", I would suggest taking a look at Rails or Django for inspiration.

In my situation, I ended up developing a framework that allowed me, when defining a model, to extend a base model class, and simply fill it with a bunch of cfproperty tags with custom properties that defined my model and were used by the base model class and a set of services which handled most of the business logic. This worked for 95% of the cases and in any situation where I needed to deviate from the framework logic, I would either add additional functions in my model cfc or even override the base model's function.

My models ended up looking like:

    <cfcomponent output="false" persistentLayer="GAE" persistentClass="asana" extends="com.bespokelogic.framework.BaseModel">
        <cfproperty name="id" type="string" persistentDatatype="string" settable="true" gettable="true" required="true">
        <cfproperty name="deckSet" type="string" persistentDatatype="string" settable="true" gettable="true" default="basic">
        <cfproperty name="englishName" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="traditionalName" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="pronunciation" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="pronunciationNotes" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="description" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="type" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="anatomicalFocus" type="array" persistentDatatype="array" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="therapeuticFocus" type="array" persistentDatatype="array" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="benefits" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="variations" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="contraindications" type="array" persistentDatatype="array" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="skill" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="instructions" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="skill" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="prelimAsana" type="asana" persistentDatatype="Key[]" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="followupAsana" type="asana" persistentDatatype="Key[]" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="thumbnailImage" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="primaryImage" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="images" type="array" persistentDatatype="array" settable="true" gettable="true" default="#arrayNew(1)#">
        <cfproperty name="primaryVideo" type="string" persistentDatatype="string" settable="true" gettable="true">
        <cfproperty name="videos" type="array" persistentDatatype="array" settable="true" gettable="true" default="#arrayNew(1)#">
    </cfcomponent>
Edward M Smith
  • 10,627
  • 2
  • 46
  • 50
  • I am already using MVC. However, I started out planning to have this subsystem reuse, but during development, there are some project specific logic/data leaks into the sub-system, and it was not easy to keep the subsystem 'clean', and ORM annotations tie to a CFC not an Interface unfortunately... Now that it's time to reuse the subsystem, I am worry how I can maintain both version down the road... – Henry Apr 15 '11 at 16:55
  • Or shall we do it like.. the Java build system? pull in the latest version of the subsystem from the appropriate SVN every time? Continuous Integration with Hudson? – Henry Apr 15 '11 at 17:38
  • I stopped coding CF right after 9 came out, so I've not got much experience with the CF ORM. It's really the business logic that you are trying to DRY up, right? I think its perfectly ok to duplicate the model/orm definitions, as there's nothing really there to test, and that's all more or less generate-able code. So, in focusing on how to DRY up significant business logic, do you have a small example we can examine? – Edward M Smith Apr 15 '11 at 17:55
  • As far as deployment (re: build system), yeah. You can use a dependency management system (we used Ivy) to specify and include the specific versions of your required base libraries. You can either use convention for your mappings, or some nifty Ant scripting could re-write your App.cfc mappings during WAR build. – Edward M Smith Apr 15 '11 at 17:58
1

I seems to me that you are trying to reuse code between two sites but there are subtle differences to the code. I know this is going to sound easier said than done, but I think you'll have to separate out the differences. So if it's only the Accounts CFC that needs to change, then maintain two version of that, one for each site.

Then you can setup some CF mappings. You can then have a set of CFCs for both sites under com.common.model and another under com.mysite.model and another under com.myothersite.model.

That way you avoid site specific conditional logic in your model layer - which would turn into a big mess very quick.

Hope that helps.

Ciaran Archer
  • 12,316
  • 9
  • 38
  • 55