The amazing adventures of Doug Hughes

Archive for September, 2005

Reaction Name Change – Also, a Request for Validation Advice

Reaction’s name has been changed. Mr. Corfield astutely pointed out another ColdFusion framework already named Reaction. So, to both reflect the “tor” in the word “generator”, and to keep just over 62% of the now-defunct name, I have rechristened it “Reactor For ColdFusion.” I like this name because it sounds more proactive than Reaction. Other than that, it was the first name I didn’t dislike in some way or another. Thanks to everyone for their suggestions.

And now, my request for assistance:

I’m getting prepared to begin a more formal process for building Reactor. Over the next few weeks I’m going to be gathering suggestions and feedback on the API’s needed feature set.

To help me understand Reactor a bit better, I’ve begun the process of writing a very simple survey system using it. I’ll use this both as a sample Reactor + Model-Glue application and a means for gathering feedback from interested parties.

The first challenge I’ve run into deals with data validation. My initial plans had been to have a validate() method on bean objects which would check values against database constraints.

I was going to steal the ValidationErrorCollection from the Model-Glue framework. This would be passed into the bean, populated with any errors and then returned.

For instance, if you had a table with a column named “emailAddress” and that column was defined as varchar(50) and not null, then the framework might generate the following chunk of code inside the validate method:

<cffunction access="public" hint="I validate this form bean." name="validate" output="false" returntype="Reactor.util.ValidationErrorCollection">
	<cfargument hint="I am the validation error collection to place errrors in." name="ValidationErrorCollection" required="yes" type="Reactor.util.ValidationErrorCollection"/>
	<cfif Len(getEmailAddress()) NOT>
		<cfset arguments.ValidationErrorCollection.addError("emailAddress", "The emailAddress field is required.")/>
		<cfelseif 50 GT Len(getEmailAddress())>
		<cfset arguments.ValidationErrorCollection.addError("emailAddress", "The emailAddress field must be less than 50 characters.")/>
	<cfreturn arguments.ValidationErrorCollection/>

There are, however, important problems with this example:

The error messages are static and not user friendly. They are in English. There is no obvious way of enforcing custom business logic such as validating the email address.

Frankly, I don’t like the idea that a non-English speaker might get an unfriendly and unintelligible error message simply because Reaction only returns unfriendly error messages in English.

So, what are the solutions to this? Well, one idea that comes to mind is an XML configuration file. Maybe this could be internationalized so that this could be swapped out. That way, the Pig Latin version might be able to return, “Ethay emailAddress ieldfay isay equiredray.”

That still leaves the problem of the unfriendly column name “emailAddress” as opposed to something like “Email Address” .

There might be a couple solutions to this. For example, I could create another XML to map column names to their descriptions. This could be in any language and might be something akin to:

    <column name="emailAddress" value="Email Address"/>

However I don’t like this. In particular, I don’t like the combined requirement to have two XML configuration files (or one big one). One of my unspoken goals in the development of Reactor is to require as little configuration as possible. Ideally, I’d like the entire configuration to consist of passing the four current arguments to the ReactorFactory’s init() method.

Why? Well, I think that much of this information already exists (or could) in the database. If you change a column name, remove or add a column, or make any other number of changes you have to make the change in the database as well as remembering to update XML files.

Granted, that’s not as bad as having to update and re-test all your data objects, but I’d like to avoid that. Also, I don’t want to prescribe a specific, singular and unchangeable solution to the problem.

I would strongly prefer to rely on implications and convention (ala Ruby on Rails) before explicit configuration. I may choose to require a specific naming scheme before requiring configuration files.

On a related note, MSSQL allows you to describe a “description” for a column. Actually, this is an extended property, and you can have any number of them. In my current project we a custom property that was the English name of the column. Our code generator (not Reactor) used that to generate more friendly error messages. I’m not sure if there are any equivalents to this in all possibly supported platforms so I’m tempted not to go this route.

Another idea I’ve been pondering is to have static constants error numbers representing speicific errors. For example, I might have a validator object which has a number of static constants defined in it, such as:

<cfcomponent displayname="XXXValidator">
    <cfset this.InvalidNumericValue=1/>
    <cfset this.InvalidDateValue=2/>
    <cfset this.StringToLong=3/>
    <cfset this.DogAteHomework=4/>
    <!--- etc... --->

This object might have a validate method which you pass a bean to (or a TO to). It would inspect the bean and determine if any errors exist. A collection would be populated relating field names to error numbers. Another component or (even the user interface) would know how to translate these numbers and field names into friendly error messages. (How? I’m not 100% sure yet.)

This still doesn’t solve the problem of custom business logic. However, that could potentially be placed in either a custom validator or in custom logic elsewhere.

I’m even tempted just to say that validation must be done manually in the Reaction-generated customizable components. That’s still easier than having to manually create a ton of objects.

Anyhow, do you have any suggestions or feedback? It would be very appreciated.

Database Abstraction Code Generators

Recently there’s been a lot of interest in code generators which create database abstraction layers. All of the code generators I’ve seen require that you run a separate program to generate DAOs, Gateways, TOs and beans. I’m currently finishing up a project where we used one.

Before I get into my experiences with database abstraction generators, let’s do a quick review of how they work. Simply put, databases contain metadata which describe their structure. By reading this metadata, you can gain enough information to generate database abstractions.

The typical code generator will be an executable which you point at a table or a database and run. The generator then creates all your needed Data Access objects, Gateways, Transfer Objects and sometimes even Beans.

I’ve been using a code generator on my most recent project. There are some things I love about it and some things I don’t like so much. But, in the end, after two months of using it, I can’t imagine life without it.

The project I’ve been working on has 38 tables. This is not a large number of tables. However, if we had manually written each of the four data-related objects we would have written 152 distinct objects, all of which are almost identical.

Furthermore, with this three person team we had less than eight weeks to build the application. That mean that between all of us, working 60 hours weeks we would have a total of 1440 programming hours. However, if you consider the amount of time spent in meetings, debugging, QAing, fixing machine crashes, taking care of sick babies and wives, etc, you realize that you’re lucky if programmers program 50% of the time. At that rate you have a measly 720 hours in which all of your development must be done. This means that, if each of those 152 database abstraction files took an average of one hour to write, you would be spending more than 20% of your entire development time writing your database abstraction.

Let me put it another way, the code generator we used was probably the most significant reason we’re going to be hitting our deadline. (Actually, we hit it a week early.)

How it works is that you open up the code generator in a web browser and select the types of objects you wanted to generate for a particular table (or all tables) in your database. You provide the path to the directory where generated files should be written and click the generate button. The generator grinds away for a few seconds. When done, you receive a report as to what it did.

There is, of course a problem with this. First off, what happens if you need to customize the objects? There’s no way (that I know of) that a code generator could generate all the queries you might need. So, if you customize the objects you would run the risk of loosing your customizations the next time you regenerate your code.

We solved this problem by customizing the code generator to generate objects into a “base” directory and to also (if they don’t already exist) generate empty shells of “custom” objects which extend objects in the base directory. This allows us to safely edit the custom shells without worry that they might be overwritten.

This particular code generator has a few short comings: It was brought into this project by one of the developers we contracted. They own the IP related to it. Also, it’s closed source and is not available to the public. Another thing that I don’t like is that it creates difficult to read code.

But still, the code generator is terrific, but why do I need to run it each time the database changes?

Why can’t I just have a simple API which does all the work behind the scenes?

If I know what I want and where it is, and it (the database) knows how to describe itself, why can’t an API just generate objects on the fly as needed?

I suppose there’s no reason it can’t. Hence: Reaction For ColdFusion – An API for Inline Dynamic Database Abstraction.

Watch my blog for more on this in a little bit.

Tag Cloud