In his landmark book Refactoring: Improving the Design of Existing Code, Marting Fowler not only presents a catalog of “regular” refactorings, he also mentions some “big” refactorings. These big refactorings are not described as a series of atomic steps to follow, but more as a recipe for using a longer series of regular refactorings. And since they are bigger than regular refactorings, they also take a lot longer to complete, sometimes even months.
I’m in the middle of one of these: Separate Domain from Presentation. Now, we all know that we shouldn’t put business logic in interface code, so why do I find myself in this situation?
Well, technically, I don’t π We use Struts, which has a nice MVC architecture. However, it’s the Controller part that has me worried. In Struts, one writes Action classes to control application flow:
“The goal of an
Action
class is to process a request, via itsexecute()
method, and return an ActionForward object that identifies where control should be forwarded (e.g. a JSP, Tile definition, Velocity template, or anotherAction
) to provide the appropriate response.”
It is, however, all too convenient to implement the business logic in Action
classes as well. The Struts documentation even warns about this danger:
“Perform the processing required to deal with this request (such as saving a row into a database). This can be done by logic code embedded within the
Action
class itself, but should generally be performed by calling an appropriate method of a business logic bean.”
And that’s exactly what’s happened in our code. So I guess I’m actually in the middle of Separate Domain from Controller π
Fixing this is not a trivial task. The Action
classes use ActionForm classes that hold data entered in the UI to perform their work. This ties them to Struts, which I don’t like at all. For instance, it makes it very hard for us to switch to a different web framework, should we so choose. It also means that simple solutions like Extract Method won’t work, since the extracted method would get the ActionForm
as a parameter.
My solution has been to introduce what I call Service
classes. A Service
class has one method that implements the service that the Action
provides. The method has one parameter, which is a Parameter Object, that contains the same information as the Action
‘s ActionForm
does. I call them Service classes, since these classes could very well be used to implement web services as well.
Anyway, all the Action
class has to do now, is:
- instantiate the appropriate Parameter Object
- populate it from the
ActionForm
- instantiate the appropriate
Service
class - call the appropriate method on the
Service
class (passing the Parameter Object) - update the
ActionForm
from the method’s result object - construct an
ActionForward
(possibly using information from the result object)
Luckily, I could automate all of that in a base class using reflection, so that each Action
class now only needs two methods: one for instantiating the Parameter Object, and one for instantiating the Service
class.
Still, that leaves a lot of Action
s to convert. And to make matters worse, they are organized into class hierarchies, which makes it hard to convert them one by one. So I guess I won’t be sitting idle any time soon…