What the Heck is a Gateway Anyway?
First, A Little Background
So, I am not the first person in the CF world to bring up this issue. Peter Bell has been harping on it for some time. Brian LeGros has also criticized the typical ColdFusion interpretation of a gateway. It is fair to criticize the way a gateway is commonly presented in ColdFusion if, in fact, it is patterned on J2EE, but is it? Or, more to the point, should it be? First, let's look at the gateway in other languages, specifically Java.
The Gateway
In both Java's interpretation and .net's interpretation, as I understand them, the gateway is an object whose job it is to connect to an external resource. For instance, let's say your application relied on an external API supplied by Google, as many do. In this case, you would create a gateway object to encapsulate this relationship and pass back the data to your application. This way, if the API changes, as API's tend to do, you need only update the code in this single location.
The Table Data Gateway
While online resources covering this pattern were seemingly limited, as discussed by Fowler as well as here (amongst others I won't waste linking to), the table data gateway is a replacement for the DAO to be used in specific scenarios. The table data gateway includes the standard CRUD methods. It also does include methods for working with the entire contents of a table in a database that might I saw referred to as "finder" methods because, you would see in Fowler's example, they are named like findByWhatever().
The Gateway in ColdFusion
Obviously I have oversimplified the discussion of these patterns in Java, however I think it is clear that the ColdFusion interpretation is not, in its common form, similar to either. Within the typical implementation that I have seen and that I use in ColdFusion, the DAO usually handles single record CRUD methods and the gateway handles anything dealing in multiple records. While the DAO contains very simplistic single table SQL statements, the gateway may contain complex SQL using table relationships, subqueries or aggregate functions. Lastly, while in most cases the DAO will generally require and return an instance of an object, the gateway may often return straight query objects (though they may return arrays of objects, this is not the norm at this point in time).
Conclusion
So, once we agree that this implementation is not the gateway or the table data gateway in Java, does that make it wrong? In my opinion, the answer is absolutely not. I think what we call a gateway is a new ColdFusion-specific pattern based very loosely on the table data gateway but not to be confused as an implementation of that pattern. Its existence seems to have come out of a necessity, in large part, created by the poor performance of creating objects in ColdFusion. By separating the DAO's single record transactions that return objects from the Gateway's multi-record transactions, we are free to return query objects from the gateway because they performed much better than attempting to create large sets of arrays of objects. The performance hit for creating arrays of objects is more or less gone in ColdFusion 8, but when working in purely ColdFusion, there may still be no need to do so specifically (I generally like it purely because I like the way Flex handles this).
Lastly, I think that the organizational aspect has definite value. For instance, the DAO remain pretty static (and easily generated or automated for that matter) while the gateway's complex multi-record queries and methods are the type that tend to be frequently tweaked and modified. Thus, I think there is a benefit for isolating these types of methods and letting the DAO simply handle its CRUD.
The point here is that not all ColdFusion design patterns are or need to be modeled after J2EE and criticizing them for not following the J2EE standard fails to take into account the uniqueness of the ColdFusion language. Yes, some people make a big deal out of saying "ColdFusion is Java," but to me that is more marketing jargon than reality. In reality ColdFusion has its own benefits and challenges that often require a unique approach to specific design patterns which, to me, is where the gateway fits in.
There are still some questions that I'd be interested in seeing answers to for the Gateway in the CF world (to get a sense of the consensus out there).
1. I want to get three items by ID (I want to edit the price of products 7, 12 and 14 on a single screen). DAO or Gateway? Why?
2. I want to delete using a filter (e.g. delete expired carts - all carts where lastupdated > a month ago). DAO or Gateway? Why?
3. If the Gateway returns a recordset and the DAO returns a bean, let's say you have a user with an Age property based on the DateofBirth column in tbl_User. In the bean returned by the DAO, you probably use getAge() to encapsulate the calculation (right?). If so, how do you display the Age for a list of users without replicating the logic elsewhere? (If you do replicate it, where do you put it, and if you put the calculation into the database, how do you handle the case where the calculation requires pulling from a web service or otherwise can't be implemented in SQL?)
Good point about the separation of concerns between simple CRUD and more complex queries, but would like to see more about how people handle the "edge cases".
Hoping this'll spark a discussion!
I can't even begin to answers Peter's questions.
You can do nice things like myObj.read() or myObj.save() and the code is really easy to understand. On the other hand, putting code that works with collections or multiple records in that linked DAO can feel really strange. I'm working on some old code where the previous developer initializes an object and then returns a collection of other objects from it. Strange.
Lately, I've started making my beans or vos completely ingnorant of storage. Then if I need to operate on the db with them, I pass them into the DAO. It makes the code using it a little longer because you have to create both a DAO instance and an instance of your vo, but it feels pretty natural to pass a reference into the dao. If you do it this way though, is there a good argument for separating the DAO from the gateway? It seems like you could do both your standard crud and your complicated query based operations in the same object without much fuss.
I ran up against the "return collection of objects vs. query object" conundrum recently. I decided to go with a collection of objects as it makes the API so much cleaner, but I definitely see the performance hit of loading up 1000 objects. Can't wait until my shared host upgrades to CF8 so I can see the performance boost everyone is talking about.
I have some gateway functions that take an argument so that you can return a collection, query or id value list depending on your needs. I find this gives the gateway a lot of flexibility.
@Josh, Where performance is an issue, consider an Iterating Business Object. Basically just add a loadQuery() method to a bean, load the query into an array within the private scope of the bean, and make the getters and setters access a given element in the array. Then add your iterator methods of choice (e.g. first() and next() - maybe an optional isLast()) and you have a single object which allows you to encapsulate all of your logic in smart getters and setters, but you're only instantiating one bean instead of 1000. I do that for everything and it really works extremely well.
I have a calendar application that needs to output the event objects only when they match a date on the calendar. So I grab all the events from calendar start to calendar end, then I'm looping over the calendar dates and pulling out only the events that match the current date. I'm having trouble seeing how I can do that with an IBO.
In general, output looks like:
<cfset ArticleList.reset()>
<cfloop condition="#ArticleList.next()#">
#ArticleList.display("Title")#<br />
</cfloop>
In the particular case you're going for, you could loop through the IBO in the same way you'd loop through a recordset, but I'm personally working on a separate EventList object which will have methods like getEvents(Date) to return a collection of events which will be easier for calendars. Keep an eye on my blog:
http://www.pbell.com/index.cfm/2007/7/18/Creating-a-OO-Calendar-Part-1
Great discussion piece. I'd would like to suggest that if a data gateway serves as nexus for abstracting database queries, why not use a single gateway class in place of many %BusinessObject%Gateway classes? Name it "QueryGateway", or simply "DataGateway" and compose this delegate class into any %BusinessObject%Service that provides list methods.
The risk is that this class could become very large very quickly, but the benefit is that you truly have a "Gateway" between your external resource (database) and your application.
I'm also a follower of the DB-agnostic Bean pattern: I work with the objects in scope and then pass them to the DAOs to handle the appropriate DB CRUD. More code for sure, but I like to keep the DB code separate.


