Transfer and IBO

December 17, 2008

When I first started out learning OO I couldn't understand why a lot of people seemed to return a query object from their gateway when they are dealing with multiple records. The ColdFusion query object is a really nice feature of ColdFusion but it just doesn't fit in with my concept of OO (I might be wrong here).

I expect to get values from an object (in this case each row in my query object) by calling methods. So instead of this:


<cfloop query="PostList">
<p>#PostList.title#</p>
</cfloop>

I want to be able to do something closer to this:


<cfloop condition="#Post.next()#">
  <p>#Post.getTitle()#</p>
</cfloop>

Why do I want to do this? Well, imagine if I wanted to show how old the post was. I could do this:


<cfloop query="PostList">
<p>#PostList.title#</p>
<p>posted #DateDiff('d', PostList.published, Now())# days ago</p>
</cfloop>

We now have logic in our view which isn't ideal. It would be much nicer to do something like this:


<cfloop condition="#Post.next()#">
  <p>#Post.getTitle()#</p>
<p>#Post.getAge()#</p>
</cfloop>

This is much more flexible because I could put some login into my getAge method so that it shows the number of  days if it is less than a month old, number of months if it is less than a year old and then number of years if older than a year. All this without having to change my view. Imagine how messy our view would look with all that logic in it!

The main reason why developers tend to use a ColdFusion query object is because of performance. ColdFusion isn't that quick at creating objects so if your recordset has a thousand records it could take a while to create a thousand objects (Adobe has improved the performance since MX6.1 and hopefully CF9 will be even better). A potential solution is to use an Iterating Business Object (IBO).

Peter Bell has released his implementation on riaforge (thanks Peter!)

As I'm using the Transfer ORM (thanks Mark!) I wanted to try and combine the two.

In my gateway I have a method call getObjectList() which calls Transfer to get a query object, which is then passed to the IBO and then returns the IBO. Here is a simplified version:


<cffunction name="getObjectList"
access="public"
output="false"
returntype="any">


<cfset var ibo = CreateObject( 'component', 'BaseIBO' ).init() />
<cfset var recordset = getTransfer().list("Post", "title") />
<cfset ibo.loadQuery ( recordset ) />
<cfset ibo.reset() />
<cfreturn ibo />
</cffunction>

I also modified the IBO code slightly by commenting out the Super.Init(argumentsCollection=Arguments) line in the init method and adding an onMissingMethod method so that I can call getMyProperty() instead of using get( 'MyProperty' ).

Now I can do use my preferred syntax and of course by creating and calling a subclass of the BaseIBO I can create my getAge() method I mentioned above.

I will need to refine my code before it is production ready but the basic concept works really well, and when ColdFusion can create objects with zero overhead it wouldn't be that hard to refactor my code.


15 comments

  1. Looks like a clean and tidy solution, nice one! :)

    Comment by Justin Carter – December 17, 2008
  2. Nice to see this being used with Transfer - seems like a great use case for an IBO!

    Comment by Peter Bell – December 17, 2008
  3. Hi Peter, good to hear from you. Funnily enough I was just reading your excellent FAQU article on Data Mapper (very nice)!

    I'm sure I could integrate your IBO better with Transfer (possibly via decorators), but for now it's working nicely.

    Thanks again for publishing it to RIAForge for us mere mortals :)

    Comment by John Whish – December 17, 2008
  4. Great! I've always think IBO and Transfer are a great match!

    It'd be even better if it is integrated into Transfer, so that IBO is returned when setting onetomany collection type="IBO". :)

    Comment by Henry Ho – December 17, 2008
  5. I like the end result but my concern is that any "smart" methods in the underlying bean have to be duplicated into the custom IBO. If I could create an IBO that *automatically* had all the correct methods from the underlying bean, I would be much more inclined to support this optimization pattern (which is really what it is - in the same way some of the J2EE patterns are purely about optimization).

    Comment by Sean Corfield – December 17, 2008
  6. +1 to Seans comment. The way I use IBOs is I use them for both a single object and for a collection of objects as I take the position that a single object is simply a collection of n objects in the special case where n=1.

    I don't think it's a "pretty" conceptual position, but by doing that you only have business objects (they just all happen to be capable of iteration).

    Of course, you might be able to do something similar with a little bit of automagic where you get your IBO to instantiate the associated business object, get it's methods and then copy the methods to the IBO. I doubt it'd be that hard to do something like that - it just wasn't necessary for my use case to add that extra complexity, and I'm a lazy, lazy programmer :-)

    Comment by Peter Bell – December 17, 2008
  7. Oh and John, I'm working on the code sample you requested for the data mapper article. I meant to do it months ago, but got flattened by other stuff. I've promised Judith I'll do it on the plane back to New York on Friday if I don't get to it before (I also owe an article for Flex Authority :-(). So you should have some code to play with for the weekend.

    Comment by Peter Bell – December 17, 2008
  8. Peter and Sean, totally in agreement. Ultimately I intend to end up with the IBO being integral to the ORM. Hence me asking for your data mapper code Peter :)

    I suppose that strictly speaking an 'product' object shouldn't be aware of other 'products' (able to iterate), and I should be iterating through an array of objects but I think that the benefits make this a worthwhile compromise.

    Comment by John Whish – December 17, 2008
  9. Is the BaseIBO production ready yet? Great work...something seems so crude about retreiving recordsets from gateways as some of the tutorials suggest...I like encapsulating as the IBOs do.

    Comment by Joe – April 17, 2009
  10. Hi Joe, I never actually put this into production for a few reasons. The main one is the duplication of code that Sean mentions above, which quickly becomes a maintenance nightmare (defeating the benefits of OO).

    Secondly the recent hotfix for ColdFusion 8 has really speeded things up, and as I'm sure Sean and Peter will be pleased to hear, it's even quicker on the latest Railo release!

    Finally, by re-factoring my code I got it only create objects for the current view. In other words if my query returned 200 results and I was only showing 10 at a time, I made sure that only the 10 that I needed were loaded.
    The IBO is a great solution, I just don't think that in the situation I had, it is the best approach.

    Have you read up on TransferObject proxies? They might help.

    docs.transfer-orm.com/wiki/Using_TransferObject_Proxies.cfm

    Comment by John Whish – April 17, 2009
  11. @Joe, forgot to mention that I know that Adam Drew was working on a Transfer/IBO hybrid but I don't know if he finished it. Might be worth posting to the list if you're interested in finding out more.

    groups.google.com/group/transfer-dev/msg/2a3a67fceffee764

    Comment by John Whish – April 17, 2009
  12. Great tip on the transfer object proxies. I read the overview and think I can figure it out. This is a bit over whelming as I've never done a OO project before, and now I'm taking on MG, Xfer, ColdSrping at once...so please forgive these NB questions.

    I get the concepts of OO, but am having problems with the mechanics of implementation.

    Anyway, if I have an Invoice object with a o2m relationship with InvoiceLineItems property (collection?) will the TransferObject proxy actually encapsulate the collection properties of InvoiceLineItems?

    Comment by Joe – April 17, 2009
  13. @John, "recent hotfix for ColdFusion 8"? Which one? Cumulative Hot Fix 2 for ColdFusion 8.0.1?

    Which operation(s) do you see CF performing better?

    Comment by Henry Ho – April 17, 2009
  14. @Henry, the server has an upgraded JVM and also has the Cumulative Hot Fix 2 for ColdFusion 8.0.1 installed.
    kb.adobe.com/selfservice/viewContent.do?externalId=kb403781

    I haven't looked at which specific operations are faster, all I know is that it is faster :)

    Comment by John Whish – April 20, 2009
  15. @Joe, I'm still learning and suspect I won't stop (and to be honest I'd be worried if I thought I'd learnt it all!)

    The cool thing about Transfer Proxies is that you just use them as you normally would. Transfer will load in the values when required.

    As a general rules of thumb, onetomany relationships in Transfer are always going to be a potential performance issue, so think twice when you want to create one. If you do need it then fair enough! The TransferProxies and Lazy load do offset the performance issues, but they don't solve them.

    One day CFML engines (ColdFusion, Railo etc) will be so fast that we can use object collections without worrying about the overhead, but until then a pragmatic approach is a good starting point.

    Of course all of this is just my opinion and as I said, I'm still learning :)

    Comment by John Whish – April 20, 2009

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.