Are you SURE you've abstracted your database access?
Here is a simple test to see if your db is really abstracted by your DAO. Rename every single field in one of your tables, make those changes in the DAO, (but nowhere else) and see if all of your forms, validations, transformations, calculations, business rules and display code still works perfectly (custom as well as auto generated code). It doesn't? Then I guess we need to spend a little more time thinking about database abstraction.
Is it worth it?
Firstly, as you are starting to see, database abstraction really isn't that easy. Typically most applications are extremely tightly coupled to the names and the structure of the database fields. So the first question must be whether it is worth the trouble of abstracting database access at all?
The answer to that is (as always) "it depends". But on what? Well, if you’re new to OO programming, this probably isn't a concept to worry about just yet. Equally, if you have a fairly simple application or one where the field names are unlikely to change often, true database abstraction is probably more trouble than it is worth.
If however you're building a highly configurable application (my applications must allow non-programmers to be able to change the data structure of the database without breaking the code), it is a necessity.
Is it Really that Hard?
Hey, how hard could this really be?! Stick a bunch of aliases into the DAO and you’re done, right? Well, you certainly can use aliases in the DAO to solve the "changing field name" problem (which to be honest is fairly bogus – it doesn't happen ALL that often). But that still assumes a 1::1 correlation between fields and attributes. Where things get tricky is when you’re using calculated attributes in your business objects that don't match exactly to your database fields.
To take a simple example, what happens if you have a date of birth field but want to display an age attribute? No big deal, a custom User.getAge() method will take care of you. But now what happens when you want to display a list of users and have a clickable title allowing you to order by age (without any manual programming)?!
Well, first, you better be using an iterating business object otherwise you won't even be able to display a list of ages without sticking some script in your service layer or all of your view code to calculate ages. But how does your application know that to order by Age your need to order by DateofBirth DESC? That is actually a fairly simple problem to solve as it is just an inverse function so you could declaratively set Age.Order = "DateofBirth DESC" in your DAO and a smart script in your base DAO to avoid putting double DESCs would see you through. The problem becomes a little more acute if you want to display by ChargeablePrice where the chargeable price uses complex logic performed on multiple price fields to determine the chargeable price for a given item.
Of course, the easiest solution is to push all of this back on to the database. It is also the most performant solution and in some ways the simplest approach. Simply create a view for each object with the attributes pre-calculated and your views are taken care of. However, this still requires some carefully handing as your DAO will have to know enough about the underlying field names to handle the inserts, updates and deletes appropriately.
However, the database approach is not optimal. It tends to tie you very tightly to a specific database platform, makes it much more difficult to work with other persistence stores, and typically db IDEs are less sophisticated than those for programming languages. You can keep SQL in version control by storing your .sql within your subversion (or CVS or – shiver – VSS) repository, but it isn’t a great solution and if you have a separate IT/DBA person/group it becomes the project from hell trying to deal with them every single time you want to make a change to the object model. On the bright side, it means the object model and business rules can more easily be reused across platforms (a CF and a .NET app) without the performance hit and complexity of having to create an underlying model web service abstracting database access for all of your applications.
If you are not going the database route, the only other option is to have a set of optimized sorting libraries so that you can make the iterating business object iterate by the calculated value of any attribute. An interesting technical challenge (to do efficiently) and one that I’m working on now. If anyone has any great ideas, now would be the time to suggest them in the comments (please!). Right now I am thinking about looping through the object to create a list or array of the actual values of a given calculated attribute against their numerical position in the internal data representation and then some kind of algorithm to order by those values and to replace them with a new set of keys (1 for first, 2 for second and so on) which could allow you to easily iterate using those key values.
But it has been a while since I took CompSci classes, and I slept through most of them (:->), so anyone with ideas or who'd even be willing to help play with this would be much appreciated. Let me know! I would even be willing to pay a modest bounty as well as to share the credit to anyone willing to help out or write the routine. Anyone interested just comment below or email to pbell at systemsforge dot com. Anything contributed will be Apache open sourced along with all of the other code I release.





Good article. I look forward to reading more on your thoughts on the matter.
At the same time, Scott Barnes makes some good points about whether or not the abstraction is really necessary in all cases.
http://www.mossyblog.com/archives/590.cfm
I'll add that every example I have seen of iterating business objects (which always ends in "It is that easy!") isn't nearly as easy as using cfoutput to loop over a query recordset and looks for all the world like ASP.
Firstly, I agree with you (and Scott) re: business benefits (I actually said as much in a comment on Scotts post the other day). However, I've got to disagree with you re: the iterating business object.
Old way:
<cfoutput query=”GetProduct”>
#Title#<br />
$<cfif SalePrice>#SalePrice#<cfelse># Price#</cfif><br /><br />
</cfoutput>
New way:
<cfset Product.first()>
<cfloop condition=”NOT #Product.isLast()#”>
<cfoutput>
#Product.get(“Title”)#<br />
$#Product.get(“Price”)#<br /><br />
</cfoutput>
<cfset Product.Next()>
</cfloop>
It is a LITTLE more work using product.get("x") instead of just #x#, but not much, and if you don't do this how would you solve the following problem:
I want four different user list templates with different filters and layouts, I also want user view. I want to display age for user in each view which is calculated and I also want to display salary which is calculated. Only solutions are looping through query in service layer or putting business logic in the db.
As you add more and more such rules, looping through the query becomes an unmaintainable mess, so thats why the iterating business object is worth it.
As for whether full db abstraction is worth the effort, 90% of use cases it is NOT. I'm working on the other 10%. As you know I'm finishing up a system to allow non technical users to generate sophisticated web apps with almost no programming (coders throw in the last few %), so features like this that allow more declaritive specification are essential to my busienss model. I'm working on a system that could create tens of thousands of truly custom web applications. For that kind of system it is worth taking a little extra time to create a more generative approach!
Oh, and bonus points for a Captcha image I can read even though I'm not a computer :-)
Since a change like this is encapsulated within your data access layer (assuming the app is architected that way), the changes are localized there. I'd say in general that is enough of a "safety net" for the possibility of changed column names (or changed platform for that matter). So while I can see the benefits of Peter's idea (and agree if column name change is a real danger), I'd also urge that anyone planning their apps creates, at the very least, an encapsulated database layer.
I agree that db columns don't get renamed often. I think you'll see from the back half of the post that I'm only really working on this because I need to allow non programmers to generate pretty sophisticated applications with aggregations and calculated values and the like so it is worth the trouble for my (pretty unusual) use case.
I did want to point out the fact that our code is often much more tightly coupled that we consider and to investigate approaches to fix that.
Chris,
Great point. Will definitely have to look into that. I'm always so busy thinking server side, I never really consider client side solutions. Will check it out. Would be lovely to hand off some of the responsibilities to the front end and would better fit with flex and ajax front ends that I'd also like to generate . . .
Earlier on in one of my first OO CF projects, I went with Business objects over queries as there were heavy business rules and it seemed to be the proper way to sort it out. We did have sporadic changing database names, the abstraction came in handy as long as it was a column change. I ran into larger issues when the relationships changed however.
When the relationships changed, I initially went with patching and fixing. After many iterations of changes, both columns and relationships, the code toppled under its own complexity and the Business Object / Abstraction concept fell out of favour with me. Perhaps I had my objects designed incorrectly from the beginning, perhaps I just hated tossing out weeks worth of working / debugged code but switching to cfquery and QoQ for my needs ended up trimming a lot of time from the development cycle.
I remain interested in your work with application generation Peter. I have had some similar ideas myself, but not the smarts nor coding prowess to actually implement the ideas successfully. As always, it is the proper tool for the proper use that saves the day.
What would be interesting is to see a list of the blogs you read in a day. I fell upon your link to
http://debasishg.blogspot.com/2006/09/is-your-doma... <http://debasishg.blogspot.com/2006/09/is-your-doma...> while following another interesting conversation and really enjoyed reading the subject material. I read it a few times and think I pretty much understand most of it. I'd like to read more of this and if you have a list of suggested blogs on this topic, I would appreciate it.
Why not hit google meself, you might ask? I think you would be better qualified to seperated the proverbial wheat from the chaff....
Please keep the posts coming and know quite a few of us are looking forward to some more snippets from you.
You asked a couple of questions, so I'm going to send a couple of seperate responses or I'll never get this out,
Re: the blogs I read, apart from all of the CF blogs and aggregators, Joel Spolsky, John Kern and Martin Fowler, I keep an eye on Jack Greenfields blog (Mr. Software factories) over at Microsoft, but he never seems to have much interesting to say. I also keep tabs on a few Ruby blogs including DHH over at loudthinking.com.
I actually find most of the "good stuff" from aggregators - in particular http://www.dzone.com/ and (for the Java world) http://www.theserverside.com/. Dzone comes up with such a wide range of interesting technical aricles it probably takes more of my time than anything else except for MXNA and Full as a Goog.
I still don't have a good list of application generation specific blogs as there aren't many and most of them don't post much, but as I build up a list I'll put it together and create a proper blog roll like I should have!
I like your first question as it is a perfect plug for my new series!! If you'd written a little ORM, changing the relationships would also just have been a matter of changing a few declaritive lines of metadata in the objects and you'd have been done. My goal is to document the key issues in the problem space, and to show approaches and code snippets for ways of solving the problems. It will NOT become a community framework (there is Reactor, Transfer, CF Hibernate and ObjectBreeze - if nic is still in the game - for that). Hopefully some of the snippets will help people to better understand what ORMs are and the design issues they address so they can select and use them more ntelligently.
Hand coding anything using OO best practices takes forever. That's why I'm so busy on the new application generator. If I don't finish it soon, my fingers will fall off from having to code so darn much while I am "in between generators"!
Interesting and thought provoking as always. I'd just like to add a comment on the debate about iterating business objects as part of the presentation tier, your reply to Steve #2 I guess. I find a fair amount of complexity in the presentation tier for apps that I'm involved in. (x-different perspectives on that SalesReport eg). I find grouped cfoutput coupled with occasional Q0Q invaluable. I can do this mostly without returning to the business object tier and coding any extra stuff in there or in DAOs. I don't see how I could achieve this if I was using iteration like you suggest.
As regards catering for column renaming, I'm with Brian and others on this. I can't think when it is really necessary, and if so it is a high impact change elsewhere. If you need to output actual column names then aliasing will probably work somewhere or other. In the case of bespoke tables/objects, I suppose you need some kind of metatdata anyway so could this not be handled like this?
( for test example <cfset metadata=structNew()>
<cfset metadata.myColumn1 = "column1">
<cfset metadata.myColumn2 = "column2"> )
<cfquery datasource="ds" name="query">
select 'testing metadata'
<cfloop collection="#metadata#" item="column">
,#metadata[column]# as #column#
</cfloop>
from tblMyTable
order by #metadata[myColumn3]#
</cfquery>
Glad you're still enjoying the posts!
I am working on a presentation IBO that decorates the core IBO but knows about things link how to properly format a phon number or a SSN in HTML. The benefit of the IBO is that you can abstract the complexity of your display logic, leaving simple templates which your designers can manage.
I have lost track of the number of times I have had to make tweaks to look and feel because it was mixed in with complicated display logic for n-column table display,alternating table row colors, formatting prices to 2dp, displaying dates using dateformat or some kind of complex logic regarding grouping. My goal is to abstract all of the property display rles into the HTML presentation IBO which will decorate the simple IBO for HTML displays (the core IBO will still exist for passing to a remote facade for your flash apps and the like). I'm working on a set of base UI controls in HTML that will abstract all of the n-column and URL creation functionality and I am hoping to be left with simple, discrete templates that designers can use safely for pages, screens, views and snippets so designers will have almost unlimited flexibility but won't have to worry about breaking the code. Will be posting on this next month!
The aliasing you suggest is pretty much what I do in my BaseDAO. Only difference is that it also supports more complex functions so if you wanted to have a property of the category object called NumberofProducts (in category), you can specify that in the metadata and it'll write the select subquery for you so you don't need to actually go get all of the products and do a count. I've needed that functionality a number of times.
Best Wishes,
Peter
Good luck with the total presentation tier abstraction. We have never managed to get that slick with it. Designers produce static look and feel, but building the final rendered page is still a development task, but really, really straightforward. I think that endless tweaking of presentation tier is a desirable feature. Especially if you can do it quickly and without fear of breaking anything else.
Cheers
Richard