Friday, March 28, 2008

My path to the Spring Framework "Aha" Moment

Many people see unit tests as a good way to get started with Groovy. Tests are quick and easy with Groovy. But for me, Groovy is just the beginning. Using Spring is what really makes it easy to keep driving down the test driven road.

There's been a lot written about dependency injection the past few years. I know many people look at dependency injection with confusion or frustration when they first hear people talk about it. They struggle to see what value it brings. Many people it just adds a layer of complexity without bringing any benefit to a project. I was in the same boat until I tried to write tests for all of my code.

I think a key architectural decision for writing testable code is using factories for object creation. I used to work on a web application where all database access was only allowed via SQL stored procedures. Not surprisingly, problems arose when the SQL stored procedure person would change the inputs or outputs and the Java programmers wouldn't find out about the change until someone trying running the application and something broke. This was the start of my journey to my Spring "aha" moment.

Step 0 - Testing from the App

At first, development and testing of Java services that invoked these stored procedures was done by writing the Java code, fire up the web app server, and navigate the screens until you hit the code under test.

Step 1 - Test with a Custom Servlet

Navigating those screens was painful. So someone created a test servlet that would allow developers to execute the code being developed without navigating all of the screens. This improved development but wasn't something was used for testing beyond initial development or bug fixing. Attempt were made to automate servlets using Cactus, but the setting up the test environment at the time was challenging.

Step 2 - Test with a Local Database Connection

The next improvement made was to add a method that could create a local database connection or get a pooled connection from the web container based on a property file setting. The default behavior was to use a container managed connection unless the "test" property was set. This really made difference in development speed. It completely removed the need to fire up the app server during development.

Step 3 - Move Connection Management to a Factory Class

Duplicating this database connection method across all of the Java database service classes wasn't attractive, so a database connection factory class was created that was responsible for the local or container-managed object creation logic. This was really valuable. There was one place to control all access to all database connections. When troubleshooting connection management problems, it was really easy to write a dynamic proxy for JDBC connection objects that logged when connections when connections were opened and closed.

Step 4 - Creating Other Factory Classes

Having experienced the value using a factory. I found myself in other areas of my application, such as security.

Step 5 - Spring "Aha"

After creating my third factory class with boilerplate code, I realized that it would really nice to have a consistent way to deal with creating and configuring all of my factories. "Aha" - Spring removes the burden of writing all of those factory classes. It provides a framework for creating different configurations for test and production. It also provides an easy way to inject code via Spring AOP across unrelated objects (cross-cutting concerns). I started using Spring and really liked the way it cleaned up my code. I liked removing the factory classes from my code base. I like removing all of the calls to the factory from the application code. Everything just felt cleaner.

Now, when I start writing a new application, the first thing I reach for is Spring. It provides the framework for writing testable code so that it will easily run outside of app container. This is important for easily running all of my unit tests from Ant - which for me is Step 0 in agile development.

No comments: