Skip to main content

Using a Service Delegate to Avoid MVC Controller Bloat

November 4, 2009

{cs.r.title}







One of the key concepts in the software industry is that of separation of concerns: the only way to reliably build any type of complex system is to break it down into small, simple, and focused components that each perform a specific function. Performing only a specific function makes each component easier to understand, develop, unit test, reuse, and maintain. You see strict adherence of separation of concerns within the standard three tier Java web application design that has come to dominate Java software development. It is common practice to use a web model-view-controller (MVC) framework in the presentation/web tier (such as Spring Web MVC or Struts2), a middleware framework in the middle tier (such as Spring), and an object-relational mapping (ORM) tool in the data tier (such as Hibernate or any JPA implementation).


width="1" height="1" border="0" alt=" " />

The whole point of using frameworks and tools is to free yourself from the complex technical challenges and boiler plate code you would otherwise be forced to write on your own and allow you to be more focused and productive while developing your application. But because frameworks and tools handle the hard parts (and are also reused in a wide range of situations), they tend to be more technically complex than the application you are using them to create. The result is that frameworks and tools typically demand much more emphasis on architecture and design, and developers working on frameworks and tools tend to be much more diligent in incorporating design best practices into their software. Once again, this frees the application developer to enjoy the benefits of having to "just" design and build an application that takes maximum advantage of all of the frameworks and tools and ensure that they all integrate and interact well not only with each other but with the application's custom business logic.

And therein lies the design gap: while the creators of frameworks and tools must invest a great deal of attention and focus on design, application developers can afford to be more lax. A classic example of the design gap is what I call controller bloat. In the case of web applications, controller bloat occurs when application developers violate separation of concerns and put non-web service code directly into their web MVC framework's controllers.

When an object receives a request, the object can choose to either perform the request itself or to delegate the request to a second object which will do the work. The industry naming of this second object varies, but I refer to it as the service delegate. This term reinforces this second object's role as a delegate, and also underscores the fact that it contains service level business logic.

This article discusses using a service delegate in combination with your MVC framework for a design that avoids controller bloat. A background with the MVC pattern is assumed, and although the examples use Java and Spring Web MVC to apply the design to a web application, the concepts apply to any language as well as to any MVC framework. The terms service, service code, service layer, and service delegate are used interchangeably to refer to your custom business logic.

The problem: MVC Controller Bloat

Using Spring Web MVC as an example, let's look at a sample of a typical web MVC controller.

[prettify]public class TypicalParameterizableViewController extends ParameterizableViewController
{
    private UserService userService;
    
    protected ModelAndView handleRequestInternal(HttpServletRequest request, 
                                                 HttpServletResponse response) throws Exception
    {
        Map<String, Object> model = new HashMap<String, Object>();
        
        String userId = request.getParameter("id");
        
        User user = userService.findById(userId);       
        
        user.setLastAccessTime(new Date());
        userService.persist(user);
        
        if (user.isAccountExpirationWithin90Days())
        {
            userService.sendAccountExpirationWarningEmail();
        }
        
        // Dispatch to the view
        String viewName = getViewName();
        ModelAndView modelAndView = new ModelAndView(viewName, model);
        
        return modelAndView; 
    }
}
[/prettify]

While the above code sample is functional, it does exhibit a major design problem: service code (i.e., your business logic) is unnecessarily located within the controller itself. The majority of the code within the handleRequestInternal method has nothing to do with the web tier and therefore violates separation of concerns. This leads to a handful of cascading disadvantages. Because the code lives in a class directly depending on the web tier, it can't be easily reused within non-web-based applications. Next, just as the code is difficult to reuse outside of a web-based application, it is difficult to unit test. While there is a plethora of options such as using mock objects or sophisticated strategies such as hot deploying code to running application servers and automated remote unit tests, that fact that your service coded is coupled to your presentation tier complicates both its testing and its reuse.

All of these problems stem from the same root cause: there is no reason to have the service code located in the controller. In my opinion, a controller should be as "thin" as possible, and only contain code that directly relates to handling the incoming request, delegating all non-request/response processing to a service delegate that will generate the model objects for use in the view, and then create and return the outgoing response. Because MVC frameworks handle the first and third responsibilities for you, by relocating service code (the second responsibility) to a service delegate, controller bloat can be completely avoided. While most architects and developers agree with this design philosophy, in practice few actually incorporate it into their software.

A Solution: The ViewService

Knowing that we want our controllers to delegate processing to a service delegate, the first step in moving away from or preventing controller bloat is to create an interface to act as a behavioral contract for the interaction between the controller and the delegate.

[prettify]public interface ViewService
{   
    public String REQUEST_PARAMETERS_KEY = "request-parameters";
    public String REQUEST_QUERY_STRING_KEY = "request-query-string";
    
    public void generateViewModel(Map<String, Object> model);
}
[/prettify]

While it's easy to be initially underwhelmed, the ViewService's power and flexibility are derived from its simplicity. Because it does not contain any implementation details of either the presentation or service tier, it's able to act as an independent intermediary without allowing one layer to bleed into the other. As you'll see shortly, from the controller perspective, all the controller knows is that it will invoke this method with a Map as an argument, and that once the invocation is complete the results will be available in the Map. From the service perspective, all the service class knows is that this method will be invoked with a Map of input objects, that it should use these input objects to generate whatever data the implementing class is responsible for, and then return the results through the same Map. By agreeing to pass only a Map between layers, each side is fully encapsulated and has no knowledge of how the other side works.

This one, simple, single line interface leads to many tangible benefits.
The use of the ViewService interface completely decouples your code from your selection of which MVC framework you use. Your MVC selection no longer needs to be a strategic or organization-wide decision because switching, or even using a separate MVC framework per application, is now trivial. Each application you create now has the freedom and flexibility to use the best framework for the given requirements and technical circumstances.

Going even further, note that a class implementing the ViewService can now be reused in any type of application: web (servlet or portal based), batch, fat client, EJB, non-EJB, and remoting applications such as web service or JMS based software are now all possibilities. This level of reuse will allow you to follow the Don't Repeat Yourself (DRY) principal, and because you are programming to interfaces, an industry best practice, your presentation and service tiers can now be built in parallel by multiple developers. Unit testing of your service code is significantly less complicated because it can be exercised through straightforward unit tests interacting directly with your service layer. This greatly simplifies testing, makes it easier to isolate issues, and saves you time.

It doesn't scream for your attention, but another subtle benefit is that this design makes it easier for new team members or less experienced developers to join and contribute to your project. For a multitude of reasons, not all developers can jump head first into an existing system and make substantial contributions right away. By having your layers and your complexity cleanly separated, you can have a particular developer work on an individual area where they are strongest or most comfortable, while they gain experience with or knowledge of a different part of the system.

With the ViewService in place, the next step is to incorporate it into your application. The following code sample shows a Spring Web MVC Controller that, through the use of a support class, delegates to an underlying ViewService implementation.

[prettify]public class ViewServiceParameterizableViewController extends ParameterizableViewController
{
    private ViewService viewService;
    
    protected ModelAndView handleRequestInternal(HttpServletRequest request, 
                                                 HttpServletResponse response) throws Exception
    {
        Map<String, Object> model = HttpViewServiceSupport.processRequest(request, response, viewService);
        
        String viewName = getViewName();
        ModelAndView modelAndView = new ModelAndView(viewName, model);
        
        return modelAndView; 
    }
    ...
}
[/prettify]

As with the ViewService, it's easy to be underwhelmed by the controller's simplicity. But once again, the controller's power and flexibility is derived from its simplicity. Holding true to the intent and vision of the design, the controller is as thin as practically possible. It's so thin, as you can see, that creating controllers for any other MVC framework is trivial and quick work.

Now that we've created an ultra thin controller, let's take a look at the HttpViewServiceSupport class that contains the core functionality.

[prettify]public class HttpViewServiceSupport
{   
    public static Map<String, Object> processRequest(HttpServletRequest request, 
                                     HttpServletResponse response, 
                                     ViewService viewService)
    {
        Map<String, Object> model = new HashMap<String, Object>();
        
        buildModelFromRequest(request, model);
        
        viewService.generateViewModel(model); 

        return model;
    }
    
    public static void buildModelFromRequest(HttpServletRequest request, Map<String, Object> model)
    {        
        String queryString = request.getQueryString();
        model.put(ViewService.REQUEST_QUERY_STRING_KEY, queryString);
        
        processParameters(request, model);                
        processAttributes(request, model);
        processCookies(request, model);
        processHeaders(request, model);
    }

    public static void processParameters(HttpServletRequest request, Map<String, Object> model)
    {        
        Enumeration parameterNames = request.getParameterNames();

        if (parameterNames.hasMoreElements())
        {
            Map<String, String[]> parameters = new HashMap<String, String[]>();
            
            while (parameterNames.hasMoreElements())
            {
                String parameterName = (String) parameterNames.nextElement();
                String values[] = request.getParameterValues(parameterName);
                            
                parameters.put(parameterName, values);
            }
            
            model.put(ViewService.REQUEST_PARAMETERS_KEY, parameters);
        }
    }    
    ...
}
[/prettify]

For any incoming request, processRequest()'s execution flow is composed of four steps. First, an empty Map is instantiated. As you know from earlier in the discussion, the central concept of the entire design is to pass only a Map between the various layers of the system (in our case the controller and the ViewService), and the very fact that this Map is just a plan old Java object (POJO) means that no layer is tightly coupled to any other. Second, the incoming request is transformed from it's current presentation-tier-specific format (in this case, an HttpServletRequest) into a more generic form. This is accomplished in buildModelFromRequest(), which extracts the interesting or useful information from the incoming request and places it into the previously instantiated Map. Third, the ViewService is invoked, and the underlying implementation performs its processing. Once processing is complete, the ViewService will store its results into the Map and thereby make the results available to any portion of the system from which the Map is accessible. Finally, with the ViewService's work complete,

the populated Map is simply returned to the invoking method. From there, it is ultimately returned to your MVC framework and, as you would expect, the normal processing flow of your MVC framework takes place.

Having seen an ultra thin controller as well as a MVC independent support class used to process the incoming request and invoke an underlying ViewService, let's next look at an example implementation of the ViewService.

[prettify]public class UserLoginService implements ViewService
{
    private UserService userService;
    
    public static final String USER_KEY = "user";
    public static final String USER_ID_KEY = "user-id";
    
    public void generateViewModel(Map<String, Object> model)
    {
        String userId = (String) model.get(USER_ID_KEY);
        
        User user = userService.findById(userId);       
        model.put(USER_KEY, user);
        
        user.setLastAccessTime(new Date());
        userService.persist(user);
        
        if (user.isAccountExpirationWithin90Days())
        {
            userService.sendAccountExpirationWarningEmail();
        }
        
        Map parameterMap = (Map) model.get(ViewService.REQUEST_PARAMETERS_KEY);        
        String day[] = (String[]) parameterMap.get("day");
        
        if (day != null)
        {
            model.put("day", day[0]);            
        }        
    }
    ...
}
[/prettify]

You'll hopefully recognize most of the above code from our original and flawed example. What's new is the use of String constants as well known key names to store objects within the Map. Finally, here is a small JSP page to put it all together.

[prettify]<%@ taglib prefix="c" uri="http://www.hdiv.org/jsp/jstl/core" %>

Welcome ${user.firstname}! 
Your last login was ${user.lastAccessTime}.

&lt;c:if test="${not empty day}"&gt;
  Today is ${day}.
</c:if> [/prettify]

Using the same well known key names as provided by the ViewService implementation, JSTL was used to pull the model objects from the JSP context and dynamically populate the JSP page.

Conclusion

Separation of concerns is a central concept in the software industry. Controller bloat, such when a MVC controller violates separation of concerns and unnecessarily includes service code, leads to many significant problems. This article presented a reusable design in which a service delegate was used in combination with a MVC framework that allowed all of these problems to be avoided. While sample code demonstrating the design used Spring Web MVC for a Java based web application, the concepts presented universally apply to all types of applications as well as software created in any language for any platform. Because any time you emphasis good design, the end result is that your code will be easier to understand, develop, test, reuse, and maintain.

Resources


width="1" height="1" border="0" alt=" " />
Eric Spiegelberg is a Minneapolis-based Java/EE consultant specializing in Spring, Hibernate, and web-based software development.
Related Topics >> GUI   |   Programming   |   Featured Article   |   

Comments

The proper way to use Spring

The proper way to use Spring mvc or any other newer mvc framework is @Controller public class TypicalParameterizableViewController { @Autowire private UserService userService; @RequestMapping("/viewSomething.htm") protected String handleRequestInternal(@RequestParm("id") String userId, Map model) { User user = userService.findById(userId); .. . ... . ... return "/p/viewname"; thanks surajz

While I agree with the use of

While I agree with the use of delegates to reduce the bloat of controllers and to make it easier change MVCs, I don't like the loosely typed Map as the communication conduit between the Controller and the Delegate. This requires the delegate and coupler to be tightly coupled. If another Controller wanted to use the delegate, that new controller to know what keys are used as inputs, the types of values for those key, which keys are used for output and the values for those keys. An alternative is to have an IDelegateArgument interface that defines a simple get(), pu() paradigm. Then each delegate takes a specific implementation of that interface. The impl has strongly type properties for the variables involved in the communication. It might have a setUser(User).