Viewing By Entry / Main
August 12, 2006
My team is building a lot of web services right now. Although we have a number of discreet groups of services, they share some common infrastructure and they all interact with a common database schema. We decided early on that we would use ColdSpring to manage the model components behind our web services and, as we've been building out the code base, we've settled on what seems to be a nice structure that allows each group of services to be developed fairly independently but still all be wired together as part of one ColdFusion application (so we can share data cached in application scope, for example).

Our basic approach is to have common infrastructure components defined in a global ColdSpring.xml file with an additional ColdSpring.xml file for each group of services. At application startup, we create the ColdSpring bean factory and then call loadBeans() for each file, starting with the global one.

We have structured our web service components as fairly simple facades. For each remote method, e.g., doSomething() we mostly just have code like this:

<cfreturn application.cs.getBean("myService").doSomething(argumentCollection=arguments) />

Most of our web services ultimately interact with a database and, at least while we are prototyping and developing, we decided to use Reactor to get things done quickly. Our global ColdSpring.xml file defines a reactorConfiguration bean with a constructor-arg that specifies the path to our global Reactor.xml and a reactorFactory bean built from that configuration bean. We wanted to be able to specify multiple configuration files for Reactor, just as we do for ColdSpring but Reactor doesn't support that. I started looking at what I would need to change in the core files in order to support this (I know, don't modify the core files!) when it occurred to me that, because we were using ColdSpring, I only had to create my own Reactor configuration object and ColdSpring would feed it to the Reactor factory object anyway. Good - I don't need to change the core files!

I created a custom configuration object that extends Reactor's regular object and overrides the getObjectConfig() method. It manages the configuration itself and has an addObjects() method that lets you add additional Reactor XML files to the basic configuration. You can download the CFC (1Kb ZIP). At application startup, as we pull in each ColdSpring XML file, we can call getBean("reactorConfiguration") and then call addObjects() on that for each Reactor XML file we need to load.

Our global Reactor XML file specifies just the database connectivity information (and the project name and mapping for the generated code). Each group of services now has a ColdSpring XML file to define the portion of the model that supports that group and a Reactor XML file to define the database objects that are used by that portion of the model.

This also makes unit testing a lot easier. We have a cfcUnit test suite object that dynamically loads test cases from subdirectories and, if it finds them, test-specific ColdSpring XML files. This test suite includes the same initialization code that we include in onApplicationStart() so that, for every test suite execution, we can establish a clean application baseline. The test-specific ColdSpring bean definitions allow us to swap out the data tier objects for stubbed test objects that return known test data, as well as mock out any parts of the model that we don't want involved in a specific unit test.

We've actually made a number of enhancements to cfcUnit, including the ability to pass an argument to each test suite so that we can control the behavior of the suite for each run. The argument is just a string that our suite code uses to determine exactly which test cases to load. We use this both from the cfcUnit web test harness as well as the ant task, allowing us to run a variety of levels of testing from simple sanity tests on just a single component up to full-blown smoke tests for the entire suite. Paul has received a copy of our changes in case he wants to incorporate them.

And then, of course, we are also using Model-Glue: Unity for our administrative applications behind the scenes. The scaffolding mechanism allows us to create quick'n'dirty applications that can view and modify the database tables, that we can later evolve into full-blown integrated administrative console applications. However, using Model-Glue with the setup described above was non-trivial. Model-Glue has its own ColdSpring bean factory and its own Reactor factory (called ormService in the core Configuration.xml file). Fortunately, Model-Glue allows you to specify a parent bean factory for ColdSpring so we were able to attach our web service level ColdSpring bean factory to our individual Model-Glue bean factories and thus make all our global model objects available to all of our Model-Glue objects without repeating any code. Very nice - thank you Joe!

We did hit one small glitch with that, however, because Model-Glue defines an empty reactorConfiguration in its core configuration file. This prevents Model-Glue from seeing our global reactorConfiguration because Model-Glue asks its ColdSpring bean factory for that bean - and finds it, rather than going on to ask the parent bean factory (our ColdSpring bean factory) for it. The empty bean is only present for Model-Glue 1.x compatibility so I opened a ticket on the Model-Glue Trac site for a workaround. It still creates two separate instances of the Reactor factory itself - one in Model-Glue and one in our global setup - but I didn't feel that was a problem worth solving for the time being.

We're pretty happy with the flexibility and power the combination of ColdSpring and Reactor provides for our web service infrastructure. The fact that we can use Model-Glue for the administrative applications and still leverage our existing external model (and Reactor configuration) is also extremely convenient.

Comments

You did ... What? Working for Adobe seems way to much fun AND you get paid.

Sean, this is a lot to take in on one reading. But I think you just opened up a whole new world. Thanks for the info.


Hi Sean, while you're on the topic of webservices, I thought I drop you a quick note to ask your opinion on an architectural issue i'm dealing with at the moment.

We currently run our web site, intranet and a custom CRM app as separate applications. Alot of things are starting to overlap and we're looking at how we can best integrate it all.

The first and probably easiest option that I see is to provide web services from each app to expose shared functionality. This has the benefit of loose coupling between the apps.

The second option I see is to combine the business models of all three to create a global domain model which can be shared by all three sub applications. What do you think would be the better solution in the long term?


@Jason, given that systems tend to change over time, I'd go with the loose coupling of web services (and that seems to be the trend for a large number of companies).


Cheers Sean. :)


Post Your Comments
Name:
Email Address:
Comments
*** Please note that all comments require moderation so it may be some time before your comment posts to this blog! ***
Remember My Information:
 



Hosting provided by