Teasing apart Struts Actions
Struts has huge market share in the web development world. Most Java web app developers start with Struts. It shapes the way we think about web applications. That would be fine except <action-mappings> in struts-config.xml is mixing concerns. I have whined about this before, but maybe this time I can explain myself.
Credit to Kris Thompson for showing more symptoms of the problem. In Front Page Controller suggestion he presents two patterns and proposes a combination:
This pattern is really a mixture of the two patterns, that being Page Controller and Front Controller. For those familiar with Webwork the Action Tag is a perfect example of it. With the action tag you can call an Action class in a JSP file instead of having the typical manner of having the Action forward to the JSP.
This pattern is great for presentation Action classes. Presentation action classes are those classes that exist just to prepare the context (request/session) for the JSP to load. It is easy to find these types of classes because they usually aren't called by a form and they only have one expected happy path forward By using this pattern I navigate directly to the JSP without having to navigate through an Action class. The strength of the Front Controller pattern is dynamic navigation so if you only have one expected positive outcome from a class then where is the need for the FC pattern?
Kris has come up with two names, and proposes a third, to describe different kinds of Actions. The need for these different names is a symptom of the mixed concerns. N. Alex Rupp gets pretty close in his outline of the concerns of a web application in WARS Architectural Style.
- Application State components that maintain the incoming data.
- Action components that can alter the State.
- Workflow components that decide the order in which Action objects execute.
- A Representation subsystem to limit the complexity of the State for component developers by restructuring or "pre-chewing" the state to make it more intuitive for component developers.
There's business data that we need to be able to look at in different ways and we need to create, update or delete data over time. State is a great way to describe this pile of data. Our application manages the changes in this State. For the sake of this discussion, I'm going to steer away from using the word 'Action'. Instead, let's say a Handler encapsulates the behavior which changes the pile of data. The Workflow maps a key part of the business logic: which changes are allowed under what conditions.
Put another way, our application is a state machine. The Workflow maps constraints in the business model, and Handlers are the transitions from one State to the next.
But there are actually two Workflows and I'm going to need another term to distinguish them. The Representation has a workflow of its own. Here's an example. Browsing the local library's online catalog, I search for books by title. The results are a list of books with titles similar to my search criteria. I click on the author of one book and get a list of the books by that author. I click on the column headings to sort the books by publication date or by title. All of these things fit within a workflow of sorts but do not change the state of the data in the catalog. I haven't created, updated, nor deleted any books in the catalog, I've only looked at different slices of that catalog. Let Workflow describe the allowed changes in the data itself, and let Navigation describe the paths I can take through the various views of that data.
In Struts an Action described in <action-mappings> can be Workflow, Navigation, Handler, or some combination thereof. Then there are Kris' different kinds of Actions which probably overlap some with the concerns I outline. Many web developers are so accustomed to thinking of Actions that they may not recognize just how many things are going on in there.
One thing JSF has gotten right is separating the configuration of the Navigation from the configuration of the Workflow, as I mentioned last year in State Machines, JavaServer Faces, bOP, and Rethinking the V in MVC. For the record, Turbine also gets this right by specifying the template and the action separately in the URL.
I'm not quite satisfied with the breakdown I've outlined. For starters, I'm not entirely sure where I would put form validation. It's clearly a crucial aspect of web application development. It touches Navigation 'cos bad input needs to bounce the user back to the form with meaningful error messages. It touches Handlers and Workflow because I want valid data before I change the data saved in the database.
Even so, <action-mappings> definitely blurs the picture in a bad way.