I was trying to come up with a nice way to introduce working with Transfer in Coldbox. At first I wanted to have a service layer to control interactions with my objects. With Transfer, however, the relations became a little fuzzy. Take, for example, an event trying to register a new user. When thinking about submitting a registration form, I normally see at least two steps: validation and saving. Trying to imagine the flow of something like this in an application that's trying to be as close to OOP as possible isn't as easy as I thought, though. Here's what I got so far.

Generally speaking, I normally see the Coldbox controllers as having as little to do as possible. Their job is a little like a traffic operator who doesn't move, but rather tells people where to go.

Some Coldbox Controller

view plain print about
1<cffunction name="doRegistration" access="public" returntype="void">
2    <cfargument name="event" type="any" required="true" />
3    
4    <!---
5        Let's suppose we have a userService object in the request collection
6        (not likely, but that's a problem for another post)
7    --->

8    <cfset event.getValue( "userService" ).saveAndValidate( event ) />
9    
10</cffunction>

You see that we're basically just passing the event argument (akin to the request scope) over to the service layer for treatment. The way I see it, the userService object is basically another controller, but for the user object rather than for the event requested.

The userService's validateAndSave() method

view plain print about
1<cffunction name="validateAndSave" access="public" returntype="void">
2    <cfargument name="event" type="any" required="true" />
3
4    <!--- do some validation stuff --->
5
6    <!--- tell the object to save itself --->
7    <cfset variables.instance.user.save( event ) />
8
9</cffunction>

I didn't want to go into too much detail for this post, but we can see that the validateAndSave method takes care of validating the data submitted and then asks the user object to save itself (passing it the event variable so it knows what values to assign itself). The user object then calls Transfer's method to update and save itself.

The user object's save() method

view plain print about
1<cffunction name="save" access="public" returntype="void">
2    <cfargument name="event" type="any" required="true" />
3
4    <cfset var locals = { } />
5
6    <!--- get the transfer bean --->
7    <cfset locals.transfer = event.getValue( "transfer" ) />
8
9    <!--- create a new user --->
10    <cfset locals.user = locals.tranfer.new( "myPackage.User" ) />
11
12    <!--- set some columns --->
13    <cfset locals.user.setMyColumn( event.getValue( "myValue" ) ) />
14    <!--- etc. --->
15
16    <!--- save the user back to the database --->
17    <cfset locals.transfer.save( locals.user ) />
18
19</cffunction>

Using Transfer's new method, we get a reference to a blank user record. We then use the generated setX methods (one for each column) to update the record, and save, well, saves the record back to the database. Simple enough, right? The question is, however, is it good enough?

Hesitations

I've been avidly following Ben's series on OOP, especially his posts (day 1 and day 2 are up, hopefully more to come) following his courses with Hal Helms. His last post talks about objects having behaviours, and being able to do what is requested from them. He also warns against « massive, procedural "Service" objects and data-driven "Business Objects" that have no true behavior ». After reading through the post a couple of times I can't help but question my structure.

  • What good is the user service object for? Shouldn't the user object be able to validate and save itself? Should it be called directly from the Coldbox Controller?
  • I seem to be passing the event object around alot... I don't feel confortable doing this and feel I'm repeating myself.

I've got some serious thought to put into this, but I'm glad I put it out there. Any insight is, as always, very much appreciated.