Here's what I said in response:
Transfer continues to evolve at a rapid pace. The SVN repository is light years ahead of the last "official" release (0.6.3) and 1.0 is "coming soon". Mark Mandel deserves huge kudos for his work on this project - and his intent to turn this into "Professional Open Source". In other words, making Transfer something we can rely on like we rely on JBoss or MySQL today.
Come to cf.Objective() 2008 to hear Mark talk in person about Transfer in two great sessions!
I know Brian Ghidinelli also ran into this issue and had started to write his own as well. I expect he'll switch to Brian Kotek's version now.
So what does it do? Well, as you build complex domain objects by writing decorators for Transfer objects, you find you need access to services that you are managing with ColdSpring. Transfer provides an event model so you can add a listener (observer) for the afterNew event and use that to inject dependencies into your domain objects. It's a fairly manual process. What Brian's CFC does is completely automate the process. You declare the injector in your ColdSpring file and pass in Transfer to its constructor. When ColdSpring initializes the injector, the injector registers itself as an observer for that event and then it automatically injects any matching services, based on setters in the decorator. Very slick!
The exception from Reactor - about the database mismatch - is swallowed by ColdSpring and reported as a bean initialization exception. If that in turn is caught and dealt with by the application's initialization code, you no longer have any indication of what the problem might be.
Fortunately, we have a great tool at our disposal to dig into these buried exceptions: the ColdFusion 8 debugger!
I fired up the debugger in Eclipse and enabled break on CFML exceptions and write exceptions to the Eclipse log. As expected with ColdSpring and its exception-happy nature, I hit a few false starts but then I hit the Reactor exceptions. There are a couple of harmless exceptions as it tests for some optional plugin points but then you get the real exception, showing up in the Eclipse log. Very convenient!
Next up I covered the Fusebox 5.5 release which is currently in limited Alpha with a public Beta planned in July (as soon as we can get enough documentation together on the new features). I also announced publicly that providing a migration path for Fusebox 3 was on the roadmap (for Fusebox 5.7 probably).
Matt Woodward (and Peter Farrell) presented Mach II 1.5 which is in Beta right now, and the new website. He also talked about plans for their 2.0 release (but didn't go into specifics).
Next up was Chris Scott, who said that an official 1.2 release would appear within a few weeks and then they would be working toward a 1.5 release. This will be the last release of ColdSpring that will run on CFMX 7 - ColdSpring 2.0 will require CF8 because they want to take advantage of cfinterface and onMissingMethod() to make ColdSpring faster (and simplify the core files).
Last up was Doug Hughes who assured us that Reactor would hit an official 1.0 release as soon as the documentation was complete. Ah, the dreaded documentation...
Brian shows how to use the factory-bean and factory-method descriptors in your ColdSpring XML file to handle automatic injection of beans obtained from factory objects that are also managed by ColdSpring. It's a powerful technique that helps completely remove a number of dependencies from your application code.
I use the same technique with Transfer and ColdSpring:
<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
Andrew Shebanow has a good post about this tradeoff in relation to Twitter, which is powered by Ruby on Rails. Alex Payne, of Twitter, has talked about their experience with the massive scalability explosion that the site has seen and some of the "friction" that they've encountered trying to refactor the abstractions out.
I worry about this in the context of most every framework I use - and there's certainly been plenty of noise on mailing lists and blogs over the years about the performance of frameworks. One of the reasons I've settled on Fusebox for the Scazu website is that it compiles all the circuit files down to inline CFML on the first request so the abstraction is compiled out (this is one of the core messages in my "Extending the language of Fusebox" presentation - see slides and sample code under 'SOFTWARE' on my blog).
I've also been a fan of Transfer for ORM, lately because of the TQL - Transfer Query Language - component that lets me write complex database queries using an object / property abstraction. It looks much like SQL but lets you use the terminology of your application instead of the terminology of the database. It does add overhead but refactoring methods that use TQL into methods that use the underlying raw SQL is pretty straightforward and can be done on a method by method basis (and, in fact, I recently refactored one performance-critical method in exactly this way).
Going back to Andrew's post (and the underlying comments from Alex), I'd say that Transfer has a low friction coefficient that contributes to why I like it so much.
For the first role, we want someone who can create crisp, clean user interfaces that are intuitive and easy to use. You'll have good graphic design skills and the ability to take a UI vision and turn it into lightweight HTML + CSS, with slick JavaScript for interactivity. If you've got ColdFusion skills, that's a bonus.
For the second role, we want someone who can build high-performance, highly scalable ColdFusion systems. You'll have a good grasp of application frameworks and object-oriented development (preferably Fusebox + ColdSpring + Transfer since that's what we use - but experience with otherwise frameworks and a willingness to learn will count).
For both roles, you will be able to work independently (and remotely, if appropriate) but with a focus on delivery and collaboration. Familiarity with source code control (e.g., SVN) and bug tracking software (e.g., Trac) is a requirement.
It's early days for Scazu so we're looking for folks who can be creative about compensation in exchange for a stake in the company. Our collective success will mean you'll be in at the ground level and able to build your own teams over time so it's a great opportunity for the right folks!
Reactor:
- Reactor implements the Active Record pattern, with objects knowing how to handle their own persistence.
- Reactor provides a rich OO-style query expression mechanism (you construct queries as OO data structures, then have Reactor execute them).
- Reactor can deduce the basic structure of your database tables for you - you only need XML to describe relationships or to alias columns and tables.
- Reactor generates "record" objects, gateways and metadata.
- To customize a Reactor object, you extend the generated object (and Reactor does not overwrite it).
- Reactor does not provide caching.
Transfer:
- Transfer focuses on business objects and provides a data access layer - you ask Transfer to load an object, you ask Transfer to save an object.
- Transfer provides a SQL-like abstraction, called TQL (Transfer Query Language), that makes handling queries of related objects really easy.
- Transfer does not introspect the database - you need to specify all of the table structure and relationships in XML, but you can also organize those object mappings into packages and have plenty of control over how the relationships are represented in the object model.
- Transfer builds your business objects on the fly, rather than laying down CFCs for you.
- To customize a Transfer object, you can either write CFML functions directly in the XML (and Transfer will include those when it generates the business objects on the fly) or you can write a "decorator" object to wrap any Transfer-managed business object.
- Transfer provides a sophisticated caching layer.
The two key new features in 0.6.3 are:
- afterNew listener hook so you can inject dependencies into newly created TransferObjects
- TQL - Transfer Query Language, modeled on Hibernate's HQL
You'll learn how we built the back end that supports several functions behind Acrobat Connect and Adobe Document Center - and Kuler - as well as some of our pain points and, in particular, the problems that arise when dealing with error handling around the boundaries of systems in a Service-Oriented Architecture.
I created a test case for parent / child bean factories and aliases, I put nothing but aliases in the child factories. ColdSpring couldn't find the aliases. I deduced (wrongly) that ColdSpring did not allow aliases in child factories to redefine beans in the parent factory.
Silly me. Turns out that my test case uncovered a bug in ColdSpring: aliases only work if the factory contains at least one real bean. At least, that's what my test cases seem to show. I'm going to run this past the ColdSpring crew for confirmation.
Want to share a single instance of Transfer between multiple Model-Glue applications?
Original code / post edited out.
The definition of transferFactory is in your parent bean factory which you tell Model-Glue about by putting this line in your index.cfm:
All the Model-Glue apps share that Application.cfc and so they all have the same parent bean factory which contains the single instance of the Transfer factory object. Each Model-Glue app has its own proxy object that delegates an alias that refers to the single factory object.
The code needs some cleanup but I figured it might be nice to get this out in front of people to get some feedback. This will eventually be the solution to ticket 25 as part of Fusebox 6.
Also in the repository are the Reactor and Transfer ORM lexicons that I showed at the Frameworks Conference. Again, these need code cleanup and enhancement but they should be a good starting point for people wanting to use either ORM library with Fusebox. A more comprehensive Reactor lexicon is also available.
The code includes:
- The ORM lexicons which provide identical syntax for accessing both Reactor and Transfer within your circuit.xml file.
- The Cat Club task manager variant that is, essentially, the Model-Glue version (ggcc7) implemented as a Fusebox 5 lexicon.
- The simplified task manager application that uses the ORM lexicons.
- The new and as yet unannounced assertions lexicon / plugin that implements preconditions, postconditions and invariants.
In addition to the documentation enhancements I mentioned earlier, the official 0.6.1 release includes improvements to the caching engine and a variety of bug fixes and performance enhancements. It also removes the earlier Model-Glue integration files because support for Transfer has now been added directly into Model-Glue itself.
Joe mentioned that he has moved the explicit Reactor ORM support declarations out of the core Configuration.xml file. In that post he said you could just declare the ormAdapter and ormService beans directly yourself.
His code base has moved on (already). If you download the very latest from SVN and you have the very latest ColdSpring from CVS, then you can take advantage of the new <alias> tag in ColdSpring!
In your Model-Glue application's ColdSpring.xml file, just add these two lines to use Transfer:
<alias name="ormAdapter.Transfer" alias="ormAdapter"/>
<alias name="ormAdapter.Reactor" alias="ormAdapter"/>
Still concerned about Transfer not being at "1.0" yet? Transfer 0.6 powers some of the machinery behind the recently released Adobe Acrobat Connect and Adobe Document Center services.
You can read all about the new features in his blog entry about the new release. If you have any concerns about trying Transfer because it hasn't reached the magic "1.0" release yet, put those concerns aside and try it anyway. My team has been using Transfer happily for a while (and will shortly be launching a new service where the persistence layer is powered by Transfer!).
As someone who has contributed extensively to Mach II, Model-Glue, Fusebox, ColdSpring and Reactor (phew!), I would like to step up and defend Joe's decisions - he's done a sterling job, sticking to his vision for the framework and has been very clear about what should be in the framework and what should not. As a framework developer myself, I can tell you it's a rocky road. The "community" deluge you with requests for all sorts of features and you have to stand firm and defend your vision. None of the popular frameworks are "kitchen sink" efforts - there are countless feature requests that have been denied by the framework authors.
I've requested enhancements to all these frameworks. Some of those requests have been implemented but most have been denied. Even as lead developer on one of the Mach II releases, some of my suggested enhancements were turned down (and some of the changed Peter implemented in Mach II were reverted as inappropriate for the framework).
When I built Fusebox 5, I was deliberately very conservative about what went into the framework and what didn't. I implemented a few things the community really wanted that I didn't think were great ideas but I also did not implement several things that I thought were great ideas that the community weren't very interested in.
Fusebox 5.1 will be a fairly conservative enhancement release. Fusebox 6 has more scope for adding features but, even so, backward compatibility will be maintained and the addition of features will be strictly controlled but community-driven.
I don't know how community-driven Mach II is. I don't think it has a public bug tracker (Model-Glue, Fusebox, ColdSpring and Reactor all do). I get the impression that Application.cfc support was added for coolness (the other frameworks have taken great pains to remain compatible with CFMX 6.1 and equivalent competing ColdFusion engines).
Third, crack open Model-Glue and edit one of the core files - shock! horror! Edit ModelGlue/unity/config/Configuration.xml and change the definitions for ormAdapter and ormService as follows:
This defines what ORM service to use, such as Transfer.
-->
<bean id="ormAdapter" class="transfer.modelglue.TransferAdapter">
<constructor-arg name="framework">
<ref bean="ModelGlue" />
</constructor-arg>
</bean>
<!--
This defines the ORM service
-->
<bean id="ormService" class="transfer.TransferFactory">
<constructor-arg name="configuration">
<ref bean="transferConfiguration" />
</constructor-arg>
</bean>
Your Transfer configuration might look like this:
<constructor-arg name="datasourcePath">
<value>/environment/transfer/datasource.xml</value>
</constructor-arg>
<constructor-arg name="configPath">
<value>/environment/transfer/transfer.xml</value>
</constructor-arg>
<constructor-arg name="definitionPath">
<value>/environment/transferdata</value>
</constructor-arg>
</bean>
Joe and Mark and I are still looking at what it will take to support relationships with Transfer in Model-Glue. To be clear, Transfer has sophisticated support for relationships but Model-Glue's ORM support is very Reactor-centric right now which makes this level of integration somewhat more difficult!
I've talked before about the structure of our service-based system: we use ColdSpring to manage all of the model components; we have simple web service / Flash Remoting facades in front of that model; we share that configuration with our Model-Glue applications using the parent bean factory feature of ColdSpring. I've been focusing on the Model-Glue side of the things this week, so I thought I'd blog about how that's working out for us.
He also listed some of the enhancements he is planning on adding shortly, including a separate configuration object which will make Model-Glue integration easier.
Model-Glue has an adapter-based architecture for ORM and ships with an implementation for Reactor. It has an ORM controller that implements the "Generic Data Messages" (e.g., ModelGlue.genericList) and relies on a Reactor-specific adapter for the actual integration with the ORM.
The ReactorORMController that ships with Model-Glue is almost entirely generic because of the adapter interface which is a testament to Joe's great design skills. Because of that, I was able to get a basic Transfer adapter up and running fairly quickly to the point that the GDMs all worked. A bit more work and I was able to get basic scaffolding working too (by creating some basic metadata and a fake dictionary object).
I haven't tackled any of the table-to-table relationship stuff but I wanted to let a broader audience know about this proof of concept to gauge interest...
Fast forward to today and Mark just committed some changes to CVS that provide a number of documentation improvements as well as the ability to turn off the internal transactions in Transfer's create / update / delete operations so that you can wrap multiple Transfer operations in your own cftransaction tags (just as Reactor allows). He has also recently added a readByPropertyMap() method that allows objects to be read based on a collection of property values (again, much like Reactor's load() method).
Reactor was the most ambitious and seems to have attracted all the attention. The other two got a bit lost in Reactor's shadow.
I recently started taking a second look at the Transfer ORM library by Mark Mandel and it's really grown up a lot since I last looked at it.
It takes a different approach to Reactor (and Arf!), not relying on the ActiveRecord idiom, but instead assuming intelligent business objects for which Transfer provides persistence. It may seem a subtle difference but it's an important one. A business object does not know how to create / read / update / delete itself - that's the job of Transfer itself - but Transfer provides the infrastructure to create and manage the business objects (as well as persist them). Transfer provides a solid event model (before / after observers for persistence operations) and has recently added full decorator support so you can write easily customizable business object CFCs that are "attached" to a persistable CFC that handles the core business object data.
Transfer also provides very sophisticated caching machinery (which I haven't fully gotten into yet).
Installation is clean and straightforward. Although Java code is used, no special attention needs to be paid to the JAR it needs because Transfer has its own class loader built in to take care of the JAR. Very slick!
It also seems to be a very low overhead ORM, in terms of raw performance.
Currently supports SQL Server, MySQL, Oracle and PostgreSQL but the approach it takes is much more generic than Reactor so adding additional databasess should be very easy. It doesn't use database introspection so there's less "magic" going on (which probably also contributes to the lower overhead in performance).
Definitely worth another look. If you don't like the ActiveRecord style of persistence, Transfer might be just what you're looking for.
Oh, and Mark Mandel created a #transfer IRC channel on DAL.net so if you have questions, pop in and he'll be there (during the Australian day time). I'll be there too.
Everything's coming together nicely for CFUNITED in terms of framework releases. ColdSpring is at 1.0 RC 1, Reactor is now at 1.0 BC 2, Model-Glue: Unity is in public beta and Fusebox 5 RC 1 will be announced at CFUNITED.
From the video, you'll see that Unity uses Reactor to drive the database access and ColdSpring to manage both the Model-Glue service components and the Reactor factory itself. A very powerful combination!
The benefits of ColdFusion are slowing becoming apparent to my (formerly non-ColdFusion) colleagues: Java integration is fairly seamless, exposing code through web services or Flash Remoting is almost trivial, high-level tags that simplify integration tasks.
Within my overall team, I run a small group focused on core infrastructure and integration and we're getting into some prototyping work now and for that we're going to use Model-Glue to build quick HTML user interfaces and test harnesses and Reactor to simplify the data access layer, with a view to moving to custom SQL queries for performance later on. Given the ease of using ColdSpring with Model-Glue to manage the service layer, I strongly suspect we'll end up using all three frameworks together for our prototyping.
Over time we'll build out Flex 2 user interfaces, probably even for the internal administrative consoles that we might initially build using Model-Glue. The service layer we build should be directly reusable in that situation.
So far, the initial response to Model-Glue, ColdSpring and Reactor has been positive and we're looking forward to Model-Glue 2.0 a.k.a. "Unity".
<constructor-arg name="configuration">
<value>/ggcc10/config/reactor.xml</value>
</constructor-arg>
</bean>
<bean id="taskGateway" factory-bean="reactorFactory"
factory-method="createGateway">
<constructor-arg name="objectAlias">
<value>task</value>
</constructor-arg>
</bean>
In the ModelGlue.xml file, specify the ColdSpring loader and that you want autowiring:
value="ModelGlue.Core.ColdSpringLoader" />
<setting name="autowireControllers" value="true" />
<cfargument name="gw" required="true" />
<cfset variables.taskGateway = arguments.gw />
</cffunction>
Magic!
Reactor is also getting some performance tweaks - some are already in the repository, more will be committed before cf.Objective() (do you see a pattern here?).
Check out the latest "BER" versions to try out the new abilities!
ColdSpring has recently been enhanced to support factory-bean and factory-method attributes in a bean definition so that you can declare beans that are actually created by external factories such as Reactor.
You declare the Reactor factory as a bean (managed by ColdSpring). Then you can declare DAO and gateway objects, in the ColdSpring XML file, which are created by the Reactory factory bean.
It's very slick.
However, most Reactor operations are focused on record objects which are transient (not singletons) and whilst those could be managed by ColdSpring, generally only singletons - per-application objects like services - should be managed by ColdSpring (since it adds a layer of complexity to object creation in order to leverage the power of the framework).
I was attempting to apply AOP to Reactor-generated objects so that I could simulate the security model in ggcc9 (the ninth variant of my frameworks comparison code - see the software pod on my blog). ColdSpring assumes that AOP-controlled objects are simple concrete classes and therefore it doesn't work with the nested inheritance hierarchy of the Reactor-generated DAO and gateway objects.
I did eventually make it work but it requires changes to ColdSpring and, for some of the autowiring I was doing, some changes to Model-Glue as well. I've submitted the patches to the revelant mailing lists for consideration but it means that I can't realistically release the ggcc10 variant's source code.
Despite my minor frustrations, the combination of the three frameworks is immensely powerful. If you look at the ggcc9 variant, you'll see it has several model CFCs and several bean CFCs. The ggcc10 variant has none of that. The model was really just DAOs and gateways which Reactor provides automatically and the beans become records which, again, Reactor manages automatically. About a dozen CFCs went away in the conversion. That's a lot of code I wouldn't have to write!
He credits me with donating MySQL 4.x support but I should point out it was only partial support and several other Reactor community members have since contributed bug fixes and enhancements for that. Oracle support is also in the works from the Reactor community (but is trickier due to how it handles auto-incrementing primary keys - or rather how it doesn't handle them but relies on sequences etc).
Steven Erat has a good write-up and link to the recording.
Doug will be presenting Reactor at the Online ColdFusion Meetup Group on January 19th (36 yes, 7 maybe). He'll also be presenting Alagad Image Component and Alagad Captcha the week before, January 12th (32 yes, 7 maybe). RSVP soon!
That means you can't have columns called: isbuilt, recordfactory, datasource, tablename, primarykey, relations, term or uilibrary.
I doubt you'll trip over those but I did - I'm building a wiki sample app and had a column called... term. I renamed it to wikiterm and all is well.
On January 19, he will talk about Reactor.
See the Online ColdFusion Meetup Group calendar for more details.

