The Value Objects

Otherwise known as a transfer object is simply a design pattern for exchanging data between disparate systems. I’m working on a application with a Flex UI and a ColdFusion backend, and I initially discovered that if I return a query directly to Flex, it is converted into an ArrayCollection populated with simple Objects.

What I wanted was an array of specifically typed objects that I could cast as value objects in Flex. Here’s a very simplistic example of an Employee value object in Flex. It has two properties:

  • Employee ID
  • Employee Name

Flex Employee Value Object

package net.fusioncube.transferObjects
{
   [RemoteClass(alias="net.fusioncube.model.employee.Employee")]
   [Bindable]
   public class Employee
   {
      private var _employeeID:uint;
      private var _employeeName:String;

      /* constructor */
      public function Effort(employee_id:uint = 0,
                  employee_name:String = "")
      {
         this._employeeID= employee_id;
         this._employeeName= employee_name;
      }

        public function get employee_id():uint
        {
        	return _employeeID;
        }

        public function set employee_id(value:uint):void
        {
        	if(value != 0) _employeeID= value;
        }

        public function get employee_name():String
        {
        	return _employeeName;
        }

        public function set employee_name(value:String):void
        {
        	if(value != "") _employeeName= value;
        }
   }
}

You may have noticed the RemoteClass metadata applied to the Actionscript class. What this does is ensure that this Flex class maps directly to a ColdFusion component value object. To make a ColdFusion component value object is simple: you use the <cfproperty> tag for each property and specify each one’s type.

In this example, I’ve also added an init() method, whose use we’ll discover later.

ColdFusion Employee Value Object

<cfcomponent displayname="Employee" output="false">
   <cfproperty name="employee_id" type="numeric" default="" />
   <cfproperty name="employee_name" type="string" default="" />

   <cffunction name="init" access="public" returntype="net.fusioncube.model.employee.Employee" output="false">
      <cfargument name="employee_id" type="numeric" required="false"  />
      <cfargument name="employee_name" type="string" required="false"  /> 

      <cfset this['effort_id']       = arguments.effort_id />
      <cfset this['employee_name']      = arguments.employee_name />

      <cfreturn this />
   </cffunction>

</cfcomponent>

Converting a Query to an Employee Array

Again, as a very simple example, let’s say I have an EmployeeService component in my service layer and have a method listEmployees() that simply queries the employees table and returns all rows.

<cffunction name="listEmployees" access="public" output="false" returntype="query">
   <cfset var effortQuery = "" />

   <cfquery datasource="#variables.config.getSetting("datasource")#" name="employeeQuery">
   select e.* from employees e
   </cfquery>

   <cfreturn effortQuery />
</cffunction>

Since I don’t want to return a query, and I certainly don’t want to have two methods in every single component - one that returns a query and one that returns an array of objects - I use the handy, dandy AOP feature in ColdSpring to apply an advice that does it at runtime.

QueryToVOAdvice Advice

In this advice, I simply loop over the query’s rows and columns to create a simple structure containing the key/value pairs for each row. It then creates a new component for each query row by passing that structure to the init() function - which you saw above - and adds it to an array.

The result is an array of Employee-typed objects.

<cfcomponent displayname="QueryToVOAdvice" extends="coldspring.aop.MethodInterceptor">

   <cffunction name="init" returntype="utility.aop.advice.converters.QueryToVOAdvice" access="public" output="false">
      <cfreturn this />
   </cffunction>

   <cffunction name="invokeMethod" access="public" returntype="any">
      <cfargument name="methodInvocation" type="coldspring.aop.MethodInvocation" required="false" />

      <cfscript>
      var local = structNew();
      local.queryRow = 1;

      // Create an array to hold the value objects
      local.valueObjectArray = ArrayNew(1);

      // Store the resulting query object from the method execution
      local.queryObject = arguments.methodInvocation.proceed();

      // Loop over the query rows
      for(local.queryRow=1; local.queryRow lte local.queryObject.recordCount; local.queryRow = local.queryRow+1)
      {
         local.voStruct = structNew();

         // Loop over the query columns and create a key/value pair for each value
         // in this row of the query
         for(local.columnNum=1; local.columnNum lte listLen(local.queryObject.columnList); local.columnNum = local.columnNum+1)
         {
            local.columnName = listGetAt(local.queryObject.columnList, local.columnNum);
            local.voStruct[local.columnName] = local.queryObject[local.columnName][local.queryRow];
         }

         // Create an instance of the value object (obtained from the getValueObject method)
         // initializing it with the structure we created.  Add the value object to the array.
         arrayAppend(local.valueObjectArray, createObject('component', arguments.methodInvocation.getTarget().getValueObject()).init(argumentCollection=local.voStruct));
      }

      return local.valueObjectArray;
      </cfscript>
   </cffunction>

</cfcomponent>

ValueObject Property

For each of my beans, in this case the Employee bean, I can simply apply an advice in the definition. For this to work, I create a valueObject property in each component.

<component name="Employee">
   <cffunction name="setValueObject" access="public" output="false" returntype="void">
      <cfargument name="valueObject" type="string" required="true" />
      <cfset variables.valueObject = arguments.valueObject />
   </cffunction>

   <cffunction name="getValueObject" access="public" output="false" returntype="string">
      <cfreturn variables.valueObject />
   </cffunction>
</component>

Employee Bean Definition

Now when I define my Employee bean, I specify the type of the value object (the type that gets returned from the init() function), and then apply the advisor (shown below).

<bean id="Employee" class="coldspring.aop.framework.ProxyFactoryBean">
   <property name="target">
      <bean class="net.fusioncube.model.employee.EmployeeService">
         <property name="valueObject">
            <value>net.fusioncube.model.employee.Employee</value>
         </property>
      </bean>
   </property>
   <property name="interceptorNames">
      <list>
         <value>queryToValueObjectAdvisor</value>
      </list>
   </property>
</bean>

QueryToValueObjectAdvisor Definition

<bean id="queryToValueObjectAdvisor" class="coldspring.aop.support.RegexMethodPointcutAdvisor">
   <property name="advice">
      <bean class="utility.aop.advice.converters.QueryToVOAdvice"/>
   </property>
   <property name="pattern">
      <value>^list.*$</value>
   </property>
</bean>

The Flex Array of Typed Objects

Here’s an image of an example array I get back from ColdFusion in one of my current apps. You can see that each object was automatically converted into a Teammate (equivalent of an Employee) rather than a simple Object.

Caveat: If you’re passing back queries with a thousand or more rows, and are running CFMX 7 or older, then this solution may cause serious performance problems as object creation is notoriously “slow” in those versions of ColdFusion.