By Peter Bell

Life without gateways - Learning to love the DAO

I believe that on the whole it should be a general case best practice to wrap all recordsets within some kind of Iterating Business Object. It ensures that if you ever need to add calculated attributes (getAge() when the db only has Date of Birth), it won’t break your application and using generic getters and setters it isn’t that much additional work.

If the main distinction between a DAO and a Gateway is that a DAO returns an object then with this approach there is no real need for a gateway.

The benefit of this approach is that there is only a single class per business object for handling persistence, so if you do want to use an abstract factory to return different DAOs for different persistence mechanisms, at least you only have one set of classes to worry about. More importantly, there can never be the slightest confusion as to where a method should go – if it relates to db access, it is in the DAO. To be honest, these aren’t huge benefits, but with no reason to have a gateway, there is no reason to add one (KISS).

The only compelling argument I have heard for a Gateway is that if you have a lot of methods (perhaps a lot of reporting style queries) your DAO interface can get pretty long, pretty quick. That is a valid concern. It is not a problem I have run into, but I’m guessing that perhaps a separate Reporting object for handling db reporting (probably one per business object – think UserReport or UserDAOReport to make it clear that it relates to persistence) might be the way to go. I’ll see if and when I have this problem.

Anyone using Gateways? Any compelling reasons to do so that I’m missing between this and my previous posting?

Comments
If you make everything use DAOs, you are just adding overhead when you return large recordsets. CF has a great query object, let it do its job.
# Posted By Rob Munn | 11/3/06 2:10 PM
Hi Rob,

I can live with the overhead of a SINGLE OBJECT for the entire recordset (not a collection of objects but a single object with iterator methods). In return it means that if I EVER want to add calculated getters I can do so without breaking my display screens. This is the whole point of OO development - trading a little performance for more maintainability!

I agree that if you can guarantee you will never ever need to add any calculated attribute displays for any of your list pages, you should just use recordsets, but unfortunately I need calculated attributes all the time.

Also allows me to decorate the IBO with a HTML aware view helper so I can format all attributes based on their custom data types automatically.
# Posted By Peter Bell | 11/3/06 2:23 PM
We've reached the same conclusion on the ColdFusion on Wheels project. We started out with a gateway and a DAO but ended up with just one big DAO acting as an IBO. Sure, there's lots of code in one place but since it's a framework the developer won't be bothered by it much since they add their customized methods in an object that extends the DAO. I don't mind having all that code in one place either since you can just use cfinclude if you want to separate it into different files. We also use an object pool so it doesn't matter much how many methods we have since we rarely instantaite an object anyway, we just request one from the pool.
# Posted By Per Djurner | 11/3/06 4:16 PM
Hi Per,

Very Cool! I'd seen posts on CF on Wheels but never had a chance to play with the code base. I really have to take the time and dig through your object pooling and the like to learn some more!!!

Thanks for the comment.
# Posted By Peter Bell | 11/3/06 4:21 PM
Go right ahead!

Although I'd recommend you wait for the 0.6 release, the code is a little "messy" right now ;)
# Posted By Per Djurner | 11/3/06 4:24 PM
Will do. Think I can keep myself busy until then. Will be looking forwards to it though!
# Posted By Peter Bell | 11/3/06 4:32 PM
I bet the code is "messy." I'm willing to be it's going to stay that way too:

"I don't mind having all that code in one place either since you can just use cfinclude if you want to separate it into different files." Yuck. That is not good. That is a warning sign.
# Posted By Yuri Liedum | 11/3/06 4:55 PM
Where's the meta data? All of these 'solutions' that you're coming up with are due to the fact that you're set on using a generic and abstracted DAL derived from the database structure. You're running into problems that you're mis-indentify as 'display' rules or the like; when more often than not they are business rules.

You should defined your _business_ objects according to how the data is used, not by how it is stored. Your objects should do a reasonable job of describing what information they hold and what you can do with it. get("someProperty") set("somePropery") do not do that. getDateOfBirth() does. If you need to provide aggregates or computational representations of data in your table, add a method for it! Add a read-only (getter) method to your bean/to/bo! There is nothing wrong with having a getAge() method with no corresponding setter.

What happens when you need to send an object to another system? Client XYZ requests a user and gets an object with 4 methods:
get()
set()
getGettableAttributes()
getSettableAttributes()

How would they know what the datatypes are? (Please don't say "getdatatype(attributeName)")

Will you have a XMLViewHelper? A BOWSDLTranslaterHelper? A BORemoteTranslaterHelper? RSSViewHelper? All just to calculate an age?

Does your IBO contain a BO for each row? Does it create one on the fly? Does it have one and repopulate it on each next() call? How and why did you come up with such a ridiculous name, Iterating Business Object?
# Posted By Yuri Liedum | 11/3/06 6:01 PM
Hi Yuri,

The metadata is in a metabase - where it should be for s software product line. There is nothing wrong with the approach you are advocating if you have days, weeks or months to build each custom application, but to get industrial levels of reuse there is no reason why the metadata needs to be based on the code. The code is just a temporary artifact required by ColdFusion to get the outcome and somewhat free form text files (cfcs and cfms) are simply not the optimal place to store anything other than the most custom functionality for my use case.

I do NOT use generic methods based on the database structure. I use generic methods based on declaritive metadata which I store in a separate metabase so that I can reuse it extremely efficiently. You go write a script to automatically reuse attributes, behaviors and rules that are hard coded into your cfcs. I'll write one to automatically generate solutions based on metadata stored in a well structured database and we'll see who finds it the most easy to automatically generate the vast majority of functionality required for future projects within the same software product line.

If I sent an object to another system, I'd generate a TO on the fly that had all of the appropriate getters and setters based on my gettableattributelist and settableattributelist properties with the appropriate typing.

The age is calculated based on a custom getAge() getter that would be a part of the model. I have an HTML View helper to handle view issues that are HTML specific and that can be described at a custom data type level (such as displaying a set of three text boxes for a US phone number or a WYSIWYG editor for a HTML description) for simple declaritive reuse of low level UI elements.

The IBO is simply a struct with a pointer (which you'd know if you had read any of the posts describing how it works). It allows encapsulated getters and setters for accessing what is basically a query (although I store it as a formal struct). Gives all of the benefits of encapsulating business rules without having to create one object for every instance in a collection (which is an issue in CF only because of the relatively high cost of object instantiation).

Why did I come up with a name for the IBO? Well, according to a recent post by Martin Fowler, I guess I could say I'm a neologiser:
http://martinfowler.com/bliki/Neologism.html

The fact is that as per GoF:

Naming a pattern immediately increases our design vocabulary. It lets us design at a higher level of abstraction. Having a vocabulary for patterns lets us talk about them with our colleagues, in our documentation, and even to ourselves. It makes it easier to think about designs and to communicate them and their trade-offs to others. Finding good names has been one of the hardest parts of developing our catalog.

That is why I named this thing. Why did I call it an iterating business object? Because at its heart it is a simple business object, but it has iterator methods allowing you to go through what has all of the necessary properties of a collection of objects for this use case (primarily displaying lists of records while still encapsulating the getter logic in case some of the attributes of the record are calculated).

I must clarify, if you are used to spending man weeks or man months creating custom business applications the "old way", many of the techniques I am focused on will seem irrelevant or unimportant. It's like Chip Temms recent article on MDA in CF. I think that MDA is a great approach, but given how little time I have to specify even fairly custom projects, it just doesn't map to my use case.

There will always be many projects that take man weeks, man months, man years and man decades to develop. I'm focused on generating a software product line with substantial variability but that would allow a single developer to build 100-200 custom projects a year. At that level, anything that can be automated must be completely automated - there just isn't time to use an IDE to write code any more except for the very specific cases where you can't reuse or modify declaritive method descriptions to generate the required behaviors.
# Posted By Peter Bell | 11/3/06 8:28 PM
Yuri,

One final thing. I go out of my way to decompose business rules into display rules, formatting rules, orchestration rules, transformation rules, validation rules, authentication rules, etc. Why? Because I can't come up with a generic DSL for rules that meets my needs. I have looked at a LOT of rules engines and I think the problem they run into is that they are trying to use too general purpose a language.

By decomposing the problem space into a larger number of smaller problem spaces, I can come up with simple DSLs for each making it easier to specify the functionality without requiring a general purpose language and therefore to store and reuse the functionality, just generating code as an intermediate step which is simpler than having to go write my own compiler for my declarative DSLs.
# Posted By Peter Bell | 11/3/06 8:37 PM
Peter,

I just think you are making your life way more complicated than it has to be. I have worked on some large CF-based apps and none of them ever warranted the kind of hoops you are jumping through to get data back from the database. Not only that, I would have never been given the budget to throw hardware at a solution I could have implemented more efficiently with less code by sticking to recordsets. But hey, you have your reasons, more power to you.
# Posted By Rob Munn | 11/3/06 11:28 PM
Hi Rob,

I just don't get the artificial distinction between encapsulating a recordset with one record and not encapsulating a recordset with multiple records. The only case that would make sense for is if you may have variability in the calculation of the attributes on your view pages but would never have variability in the attributes on your list screens. While that might be true occasionally, as I'm creating a generalized solution I can't just assume that will be the case.

I have more time for the argument that for certain projects you don't need to encapsulate recordsets at all in which case down with the DAO and make everything a recordset. That is certainly a valid approach although it has maintainability implications.

FWIW, there are no patterns I'm adding that don't solve a specific problem for a specific client or class of clients I've already coded a project for, so I do have a method to my madness. Hopefully when I finish this all up it'll start to make some more sense!
# Posted By Peter Bell | 11/4/06 12:33 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.