Object-XML Mapping in Spring 3.0: when indirection gets out of hand
Meet the Object/XML mapping support in Spring is an introduction to the Marshaller and Unmarshaller interfaces that have been rolled into Spring 3.0 from Spring WS.
In summary:
- Application code uses the two interfaces and delegates the object-to-XML-and-back (or OXM) work to them.
- The application context is configured to use Spring’s wrapper around an OXM framework.
- The wrapper is injected into the bean.
- The OXM framework also needs to be added to the classpath and configured.
To me, there are two levels of indirection too many here, already. Add another level if the application wraps this up in an XmlService interface and corresponding implementation.
Furthermore, it turns out that the abstraction is leaky! From the Spring WS docs:
Although the marshal method accepts a plain object as its first parameter, most Marshaller implementations cannot handle arbitrary objects. Instead, an object class must be mapped in a mapping file, registered with the marshaller, or have a common base class. Refer to the further sections in this chapter to determine how your O/X technology of choice manages this.
Despite all that indirection, you still need to know which OXM is being used. If you have to know what you’re using, why not work more directly with the OXM and avoid the abstraction theater?
I would go so far as to argue that, not only is the indirection not buying you anything, it’s probably going to end up costing you, by making the application’s wiring more difficult to understand and maintain.
I’ve been working with Guice of late. Its focus on programmatic configuration encourages you to “touch” 3rd-party APIs directly. This feels lean and natural compared to Spring’s often unnecessary infinite indirection.
Apply DAO design to XML and you’ll find that a few simple methods are enough to decouple you from your OXM in the same way a DAO decouples your from your ORM. For example:
void save(Object entity) // serialises to XML
<T> T get(Class<T> myClass, String xml) /* converts from XML to object of type T. Replace String with File or InputStream as needed */
Even if for some bizarre reason you need two OXM frameworks at the same time, the XML DAO should be injected with both OXM classes and decide which one to use based on the class of the object passed into the save() method.
To be really fancy, apply the Interface Segregation Principle (PDF): have the XML DAO implement two interfaces and let clients choose the one they're interested in. These might be drawn from the problem domain (UserXmlDao, ArticleXmlDao) or the solution domain (CastorXmlDao, JaxbXmlDao) [1].
In conclusion: indirection has advantages, but it also has a cost. Using it indiscriminately for trivial things leads to accidental complexity and confusion through over-engineering.
[1] See Tim Ottinger’s paper on variable naming for the concept of problem and solution domains