Modelling a properties map with Transfer
March 16, 2009
I'm currently designing an application which utilises Transfer ORM. My application has multiple User types, each of these types can have their own unique properties. (If you've read Object-Orientated Analysis & Design, then think of the Guitar Inventory example).
The solution I've decided to use is to have a "map" of properties, which I can use to allocate values to the individual user. When I started to build this with Transfer, I initially created a onetomany relationship between my User object and UserProperty object. This works, but means that you end up creating a lot of Transfer objects, which are unique to the individual User. This is a potential performance problem, so another solution was required.
Transfer.xml Snippet of the onetomany approach:
<package name="member">
<object name="User" table="USERS" decorator="com.model.member.User">
<id name="UserID" column="user_id" type="GUID" generate="true" />
<property name="Email" column="user_email" type="string"
nullable="false" />
<onetomany name="UserAttributes" lazy="false" proxied="false">
<link to="member.UserAttributes" column="user_id" />
<collection type="struct">
<key property="Key" />
</collection>
</onetomany>
</object>
<object name="UserAttributes" table="USERATTRIBUTES">
<id name="UserAttributeID" column="userattribute_id" type="numeric"
generate="true" />
<property name="KeyName" column="userattribute_key" type="string" />
<property name="Value" column="userattribute_value" type="string" />
</object>
</package>
My next attempt was to stop using Transfer to handle the composition, after all, my UserProperties are not rich business objects so there was really no benefit to using Transfer for this and instead populate a struct of User Properties in the User Transfer decorator.
Transfer.xml Snippet with the relationship removed:
<package name="member">
<object name="User" table="USERS" decorator="com.model.member.User">
<id name="UserID" column="user_id" type="GUID" generate="true" />
<property name="Email" column="user_email" type="string"
nullable="false" />
</object>
<object name="UserAttributes" table="USERATTRIBUTES">
<id name="UserAttributeID" column="userattribute_id" type="numeric"
generate="true" />
<property name="UserID" column="user_id" type="GUID" />
<property name="KeyName" column="userattribute_key" type="string" />
<property name="Value" column="userattribute_value" type="string" />
</object>
</package>
User Transfer Decorator
<cfcomponent output="false">
<cffunction name="getProperty"
access="private"
returntype="string"
output="false"
hint="I return the property value">
<cfargument name="key"
type="string"
required="true"
hint="I am the key name." />
<cfset var properties = getProperties() />
<cfreturn properties[ key ] />
</cffunction>
<cffunction name="getProperty"
access="private"
returntype="string"
output="false"
hint="I return the property value">
<cfargument name="key"
type="string"
required="true"
hint="I am the key name." />
<cfset var properties = getProperties() />
<cfreturn properties[ key ] />
</cffunction>
<cffunction name="populateProperties"
access="private"
returntype="void"
output="false"
hint="I set the properties for this user">
<cfset var result = "" />
<!--- we are doing it like this for performance --->
<cfset result = getTransfer().listByProperty( "member.UserProperty", "UserID", getTransferObject().getUserID() ) />
<cfset variables.instance.properties = {} />
<cfloop query="result">
<cfset variables.instance.properties[ result.keyname ] = result.value />
</cfloop>
</cffunction>
<cffunction name="getProperties"
access="private"
returntype="struct"
output="false"
hint="I get all the properties for this user">
<cfif !StructKeyExists( variables.instance, "properties" )>
<cfset populateProperties() />
</cfif>
<cfreturn variables.instance.properties />
</cffunction>
</cfcomponent>
I can now define concrete public methods to get specfic property values or make use of onMissingMethod to handle this for me.
Performance wise this is much better than my initial approach whilst maintaining the flexibilty of my design. I don't know yet if it will be my final solution to this problem, but so far it looks promising.
I can't end this post without thanking in particular Paul Marcotte, Bob Silverberg and Brian Kotek for their valued advice. If I get to be half as clever as those guys I'll be happy!
- Posted in:
- ColdFusion
- OOP
- Transfer
4 comments
Leave a comment
If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)





I see you've taken it a step further and are actually populating variables.instance.properties with the object's native properties.
Instead of all that, couldn't you simply do:
<cfset StructAppend(getProperties(), getTransferObject().getPropertyMemento(), true) />
Is there any reason why this would be considered bad practice?
Comment by Dutch Rapley – March 17, 2009
getMemento() and getPropertyMemento() are not officially supported in Transfer. They are there for debugging and are subject to change (this has already happened) so I'd strongly advise against using them.
I might be misunderstanding your comment, but I would need to create each Transfer object in order to get it's properties which is what I was trying to avoid. Using getTransfer().listByProperty() only creates one object for all the UserProperties.
Comment by John Whish – March 17, 2009
Also, I should have paid closer attention to your example before chiming in. I now see that you're retrieving member.UserProperty in getTransfer()listByProperty(). (Though, I'm glad to see this b/c I didn't something similar in a decorator, and at first it felt dirty)
I'm fairly new to Transfer, please forgive my ignorance. :)
Comment by Dutch Rapley – March 17, 2009
/>
Like you I'm new to Transfer so hopefully my advice is good! :P
Comment by John Whish – March 17, 2009