Layering a Spring MVC Application
One of the first things we are taught when building web-based enterprise applications is to layer our app. There are many reasons to do this, and they mostly boil down to making code easier to read, more reusable, and easier to test. This post will present a framework for layering Spring MVC applications, but it can easily apply for any Java web-based applications.
This will contain classes that span multiple layers. Data transport, entity, and util classes that are not specific to http, hibernate, or JSP belong in here.
Most applications have a datastore, whether NoSQL, MQ, or most commonly RDBMS. The idea is to create a set of interfaces that abstract data access specific technologies. The implementation translates the underlying data model into a canonical representation (typically entity or data transport objects, but also application specific exceptions). You can have more than one implementation for a given interface. For instance, you may initially implement a DAO as JPA but determine that JPQL is too inflexible and create a JDBC implementation.
With Spring, you can dispense with the implementation and leverage Repository interfaces if you are working with an RDBMS under the hood. With this approach you extend a base interface. There are several; typically a PagingAndSortingRepository or JpaRepository will be your best bet. As long as your entity classes are annotated with Jpa or Hibernate mappings, the basic find, create, read, update, and delete functions will be created for you. And you can easily add customer finders to meet all of your data access needs.
For JMS, AMQP, NoSQL, or other data access, you should use a similar approach, but you will have to create the implementation logic yourself. Spring provides a Template pattern that makes it easy to map from many different formats, including JmsTemplate, AmqpTemplate, LdapTemplate, and even a FacebookTemplate.
This tier may or may not be relevant to your application. This typically is required when you need a more coarse grained interface into the underlying data access layer. For instance you may be implementing a payment system that debits one account and credits another. The logic for each individual operation is encapsulated in a DAO, but the composite function of process payment appears as a single method in a business tier class. Many operations however simply delegate wholesale to an underlying DAO class. If 100% of your business tier classes do this, then you probably don’t need this layer at all.
One of the primary practical benefits of building this tier is that it becomes a natural place to initiate database transactions. The transaction starts here and propagates to each DAO call, and if a failure occurs on a second or third call to the database, the spring container will rollback the prior work that was done.
These classes are POJOs that are annotated as @Component or @Service. Other than this, they should have no Spring dependencies. Even though this is commonly thought of as a service tier, remember that these are not web services. Those are implemented in the presentation tier below.
This tier interacts with the business/service tier (or possibly directly with data access in simple applications). The only role of presentation tier classes is to mediate between clients and the services. Clients will typically interact via HTTP(S), whether consuming HTML, XML, or JSON. The primary classes in this package are controllers, but can include other helper classes, error handlers, validators (although these may more appropriately belong in the common package/layer), and any custom binders. Controllers are POJOs that utilize class level annotations such as @Controller for web controllers or @RestController for web service controllers. These classes rely on a series of annotations such as @RequestMapping, @RequestBody, @RequestParam, @PathVariable, @ResponseBody, and @Valid to map Java methods into URI patterns. They should contain no business logic and typically should do little more than delegate business logic to a business tier component and store results on a model instance before rendering the appropriate view.
The view tier is typically not Java code at all, but rather some view technology such as JSP, JSTL, Tiles, Velocity, FreeMarker, JSF, or other HTML rendering languages. These views combine formatting for web browsers with embedded content rendered by the model generated in the Controller class. There should be no scriptlet here, only tags that render the models into properly formatted HTML.
In the above diagram, dependencies flow from higher level to lower level layers only. The presentation tier can reference the business tier, but nothing else, and not in the other direction. Generally all layers can reference the common tier and it should not reference any classes in the specific tiers. Furthermore, only the data tier can reference data related classes such as Hibernate, JPA, JDBC, JMS, or other data implementation specific libraries. And only the presentation tier can reference any HTTP, JSON/XML Serialization libraries, etc. By following these guidelines the application architect will make it easier for developers to write maintainable, extensible code that can be tested in or out of container. Future posts will look into how to set up tools to support these layers, how to test these applications, and will dig into more details on each tier.