This article is based on Liferay in Action, to be published on August, 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks ]
Introduction
Liferay’s Service Builder encourages the proper layering of functionality within a portlet application. Generally, when building an application, it’s good practice to separate the various layers of the application: UI, model, persistence, and so on. This is sometimes called separation of concerns. By keeping the layers as separate as possible, you gain the ability to change the implementation of any one layer more easily—if for some reason you find a better way to do it later.
To some, this must seem like some unnecessary work, but let me give you an example as to why this is important.
The spaghetti code story
Suppose you have a poorly designed application that consists of only two layers—your JSPs and your portlet class. You develop the application by creating action URLs in your JSPs, which correspond to various methods within the portlet. These methods use JDBC to connect to a database and select, insert, update, and delete data. Your database, validation, and page navigation code is all in the portlet or the JSPs, resulting in very large files with lots of logic in them. You test the portlet as well as you can, and it then gets deployed to production.
Within the first day of its use, a bug is found. Your application has a last name field, and a user tried to insert the last name O’Bannon. Because you coded all of your database interaction manually, you forgot that sometimes you need to escape out certain characters. In this case, the apostrophe in the name is causing your database insert to fail.
With the poor design you used for your portlet, you’ll now have to modify the Insert and Update methods of your portlet class and add some code that checks the values your users are inserting for apostrophes. This is an example of a tightly-coupled design: your database code is inside your portlet, so your implementation of code that communicates with the database is intertwined with your business and display logic. Because you were recently working on this portlet, you know where the problem is and so you fix it and redeploy the application. The users work with it for several months before another bug is found. It seems that, somewhere in the portlet, a database connection isn’t being closed and is causing the database server to run out of connections.
Now some significant time has passed, and you’re onto another project. You don’t remember exactly what you did when you wrote this one. You now have to slog through mounds of spaghetti code in your one portlet class looking for the problem—database code mixed in with all kinds of other functionality, such as JavaScript functions, field validation code, database access code, and so on. It’s become a hard-to-maintain mess. After hours of debugging, you finally find the condition where the database connection wasn’t being closed and it was part of a long block of if-then-else conditions, which included field validation logic, business logic, and persistence logic. It took you almost an entire day to find this one problem, because it was buried in a lot of code that should have been separated out into different layers of code logic.
If you had properly layered your application so that the UI code was in one place, the business logic was another, and the persistence logic was in another, you’d have a much more maintainable project. And you might decide at that point that you could use something like Hibernate for your database persistence, and you’d only have to replace a few classes in the one layer. Doing that would solve all of your persistence problems, because Hibernate escapes characters and closes connections automatically. You might then go another step further and replace your homegrown application logic with an MVC framework like JSF or Struts. On each refactor, you could modify one layer of your application at a time. Let’s see how to do that for the persistence layer.
Using two layers for persistence
While the above is a simplistic example, it serves as an example we can use to show how Service Builder encourages proper separation of concerns. For database persistence, two of these layers are the Data Access Object and the Data Transfer Object (see figure 1). These two layers can be written by developers manually, and often a lot of time is spent writing and debugging code in this layer. Service Builder automatically generates both layers for you.
Figure 1 Service Builder generates for you everything from the service layer down. You’ll need to modify/customize some of this code, but you won’t have to touch most of it. This significantly increases developer productivity.With Liferay, the layer you’ll work with for the most part is the Data Transfer Objects (DTO) layer. This layer talks to the Service Layer, which is automatically generated, and allows you to work with objects that need to be persisted or have been retrieved from the database. You’ll call methods from the Data Access Objects (DAO) in the persistence layer to do the actual persisting. In another article, I’ll walk through how this actually works.