As Java technology has matured over the last few years so have
we. We've learned that building complex enterprise applications that
respond to change requires more than standardized APIs and virtual
machines. Fortunately, we're now starting to see the widespread
adoption of best practices, patterns, and even frameworks with
templates and prebuilt components.
This article looks at the MVC design pattern and reviews its
implementation in Struts, a presentation-tier application framework,
as well as recognizing analogies of MVC to a well-formed, EJB-tier
framework that Struts can be integrated with.
MVC for J2EE
Struts is an implementation of the presentation tier using
MVC. The Struts controller encapsulates the presentation logic,
handling the interaction of the JSP pages view with the JavaBeans
model, which might access a database through JDBC.
When we integrate with a business tier, the recommended
pattern is to access a Session Façade or Business Service - analogous
to an MVC view. The model could be thought of as our data access
objects or entity beans. The model can be queried by the view, but
updates occur through the controller, which would then represent our
business logic.
When we consider the applicability of the MVC pattern to the
business tier, it enables us to recognize the separation of
responsibilities between the interface, model, and business logic. It
demarcates responsibilities across our infrastructure components. We
see, for example, how to use different interfaces or views for the
same model or controller.
Struts Implementation of MVC
Struts delivers on its promise of a framework that follows
the MVC design pattern by effectively segregating the various
components into a small set of extendable classes. Also note the
declarative nature of its XML-based action mappings and JSP tags.
Figure 1 illustrates the interaction between the key Struts
components as they relate to editing user information.
Figure 1:
Figure 2 illustrates how each major component of Struts maps
into the MVC.
Figure 2:
The Model
The Struts implementation of the Model comes by way of the
abstract class, ActionForm. It manages the data required by the View
(JSP) and changes its state according to the Controller (Action). In
its simplest terms, the ActionForm is a standard JavaBean with
getters and setters used to access its state. It defines no abstract
methods, but you have the option of overriding the validate method to
perform server-side validation of the application data.
An ActionForm used to maintain data about a user may look
like Listing 1.
The View
The extensive Struts tag libraries combine to create a
mechanism of presentation and interaction that allows developers to
develop, and designers to design. The JSP will use the ActionForm
(Model) as a means of allowing the client to interact with the
application.
Listing 2 provides an example of a JSP page with Struts tags.
The Controller
Two Struts classes make up the Controller, ActionServlet and
Action. Both work in concert to manage the flow of the application,
but have distinct differences. The ActionServlet is responsible for
mapping URI requests to specific Actions, while the Actions are
responsible for interacting with the Model (ActionForms).
The developer extends the Action class to implement logical
units of work that will be invoked by the ActionServlet upon
receiving a request. Saving user information may be part of the
SaveUserAction (see Listing 3).
UserDelegate is a class based on the J2EE Business Delegate
design pattern. It hides the implementation of the business-tier's
view of the model and will be explained in detail later in this
article. The important thing to note is how the Struts model
interacts with the business-tier's view - the action is the bridge
that joins the two.
The ActionServlet is made aware of the URI to Action mappings
via an XML configuration file. This file describes request URIs and
how the ActionServlet should dispatch them. A typical mapping is
provided in Listing 4.
When the ActionServlet receives the request such as http://myapp/SaveUser.do from the
EditUser.jsp, it understands that its mapping corresponds to the path attribute defined in the XML configuration file, and that it should invoke
the perform method of the SaveUserAction class. The attribute "name"
defines the ActionForm (model) that the action will receive and the
scope attribute defines where the form will be found (request,
session, context). It also defines URI mappings that the Action will
use to determine where to forward. By keeping the application
mappings external, the Struts framework can be easily modified
without recompiling classes.
Online Business Systems has used the Struts application
framework on many successful projects including Viewtrak.com, an
integrated food source management tool that tracks production of
cattle from producer to consumer. The architect, Curtis Linton of
Online Business Systems, used Struts as the Web application framework
and the Versata Logic Suite for business logic development and
management. Example code from this project has been used in this
article, and updated to current versions.
Business-Tier Pattern Framework
Naturally there are many approaches to implementing
business-tier design patterns such as developing infrastructure
components and business components.
First, to develop the business components we should consider
a declarative approach, a business rules approach. Barbara von Halle
in her book, Building Business Rule Systems, states, "It is no longer
desirable to bury rules in specifications and program code where they
are locked away, requiring costly intervention and overhead to effect
change."
Second, we need to pay careful attention to J2EE best
practices to avoid those performance pitfalls we've read about: too
many fine-grained entity beans, caching, use of CMP, and so on.
Business-Tier Design Pattern Framework
Figure 3 illustrates a well-formed design pattern framework
for the business tier.
Figure 3:
What follows is a discussion of this pattern framework in
terms of the core J2EE design patterns. Using this framework, we'll
also look at how products within the Versata Logic Suite leverage and
implement these patterns and how they accelerate and simplify
business logic development and maintenance.
Within the Versata Logic Suite, business components are built
with the Versata Logic Studio from business-logic specifications,
especially business rules. Java artifacts, such as value objects,
value object assemblers, business objects (entity and session beans),
and data access objects, are produced as well as deployment
artifacts, such as database schemas, deployment descriptors, and test
data scripts.
The business components may be accessed as regular Enterprise
JavaBeans through the Versata Transaction Logic Engine or through the
Versata Logic Server that includes the framework that follows the
patterns we describe below. The Versata Logic Server also includes
the optional Versata Presentation Engine, which supports the
model-based development of HTML servlet (presentation tier) and Java
(client tier) Web applications.
Business Delegate
Business Delegates within the presentation tier hide the
remote method invocation and interaction with the Business Service
pattern on the business tier.
The Business Delegate pattern represents the Model of our
presentation-tier MVC pattern.
Business Service
The Business Service is the component that presents the
actual business functionality to the outside world. This could be
through RMI to an EJB, through messaging with JMS, or perhaps through
SOAP to a Web service.
As an interface into our business logic we would consider
Business Service one of the View components of our MVC-like pattern.
Value Objects
While a client may access EJBs directly via RMI calls to
attribute getter and setter methods, in practice this conversational
approach is expensive. A "value object" is an encapsulation of
business data that can be serialized, allowing the client to make one
RMI call to retrieve or update a set of related data.
The Versata Suite enables value objects to be created
declaratively through the Versata Logic Studio, allowing attri-
butes to be selected from objects in the entity object model, and
creating composite objects by joining across object relationships.
Value objects are also created for each entity object by default.
These value objects are also updatable, encapsulating
behavior that allows them to interact directly with the Business
Service, such as update, create, delete, and refresh. This is also
true for composite objects - updates may be distributed across
different business objects. They're also metadata value objects
accessed somewhat like javax.sql.RowSet objects.
Considering the MVC pattern, value objects represent the
view. This suggests to us that they should be model-independent, not
encapsulate business logic. We should look to create value objects
that represent how a system presents itself to the outside world as
opposed to necessarily re-presenting internal models.
Value Object Assembler
Value objects are supported at runtime by the Value Object
Assembler that builds and aggregates them from underlying business
objects.
Would this component be a view or a controller? Value Object
Assemblers typically are involved only in query operations, so they
would be equivalent to a view component.
If we were more faithful to the MVC pattern, the view objects
would access the model directly for querying state, only accessing
the controller for updates. In fact, we sometimes see this pattern as
a performance optimization, especially for accessing large sets of
data. The value object will pull data straight from the data access
object or JDBC, alleviating the need to instantiate an entity bean
for each object instance.
Our implementation uses this optimization transparently. The
Versata Value Object Assembler instantiates only business objects for
query operations when it has to. Since it knows attributes are
persisted or calculated, it infers whether it can retrieve data
straight back from the data access object or whether business logic
needs to be executed.
Data Access Object
Enterprise applications need to access data from multiple
sources, SQL databases, LDAP servers, and legacy systems, for
example. Data access objects form an abstraction layer and an
encapsulation of the data between the data source and the business
objects. This allows the business objects to use the same API no
matter where the data is coming from.
You can write data access objects or use tools. A good
example is IBM's VisualAge for Java (VAJ). VAJ's Enterprise Access
Builder allows you to create data access objects for sources ranging
from DB2 to CICS.
The Versata Logic Server has a powerful implementation of the
data access layer. Access objects are created at runtime based on
object metadata, runtime configuration properties, and transaction
requirements. The administrator can change the data source at runtime
and tune options, such as whether to use prepared statements, the
prepared statement cache-size, connection pooling, and lock isolation
levels, giving a great deal of flexibility and high performance
without requiring hand-coded BMP.
Data access objects clearly represent the Model of our
MVC-like business-tier pattern.
Business Objects
The components of the Business Object pattern encapsulate the
business logic of our system. For J2EE enterprise applications, these
components will usually be session beans, entity beans, and dependent
object JavaBeans. We use the term business objects to describe these.
The J2EE pattern recognizes that business objects could also be data
access objects or JDBC helpers. For our framework these would be part
of the Data Access Object pattern.
The business objects operate within the EJB container of the
J2EE application server and use the services for coordinated
transaction management and persistence, for example.
The Versata Logic Suite supports the automated creation of
business objects from specifications. The system sequences,
optimizes, and translates these specifications or what we call
business rules into the business objects - the Java components.
Typically some custom Java coding is required, which might be entered
directly into business rules or used to subclass or add code to Java
components. We typically see about 98-99% of the Java component built
directly from rules.
Looking at the MVC patterns, would business objects be akin
to the model or the controller? You could argue that they're part of
the model, the intrinsic representation of what the business is. In a
way though that's what the whole business tier represents. We prefer
thinking about business objects as the controller. Recall that for
the presentation tier, the controller provides the presentation
logic. For the business tier, the business objects handle business
logic, providing a clean separation from the data (the model) and
from business services (the view).
Declarative Business Logic
What Not How - Declarative Not Procedural
As elaborated by C.J. Date in his book, What Not How, there's
a move away from procedural programming - coding the steps of how we
accomplish something - to the declarative - just saying what we want
to accomplish in the first place and letting the system work out the
best way of doing it.
An example of declarative programming is the EJB deployment
descriptor, specifying what fields should have their persistence
managed by the container. EJB 2.0 goes further by adding declarative
support for container-managed relationships; something you have to
manage programmatically today. We've also seen the Struts action
mappings and view definitions as declarative approaches.
Automation of Business Rules
Consider the business requirement that the account balance is
total billed invoices less payments received. Taking a procedural
approach, we could then write code to update the account balance
every time a payment is received or an invoice is sent out. What
about when an invoice is cancelled? What about a cancelled invoice
that's reinstated? What about deleting a payment? What about when a
payment bounces? We need to be conscious of a number of side effects
with a procedural approach. What about when the invoice's account
changes? You need to subtract from one account and add to another,
but only if the invoice has been billed.
Declaratively we might express this logic as:
- Account Balance=Total Billed-Total Payments
- Total Billed=sum (Invoice.TotalFee where InvoiceStatus
="Billed")
- Total Payments=sum (Payment.Total where PaymentStatus
="Active")
These are called business rules. Taking into account all side
effects, these rules require over 300 lines of Java code across the
Account, Invoice, and Payment objects. Listing 5 provides a small
portion of the code built using the Versata Logic Suite as it appears
in the Invoice object. The code implements logic to deal with a
change in the InvoiceStatus of TotalFinalFee of an invoice and the
impact on TotalBilled of an account.
Constraints are another type of business rule; they represent
the policies under which a business can operate. Figure 4 shows
constraints for the Customer object.
Figure 4:
In business rules terminology we call these calculations
derivation rules - the attribute definitions are derived from other
attributes. Figure 5 shows several derivation rules for the Account
business object, providing detail from the Total-Billed sum rule.
Additional derivation types can be seen in the grid: a replicate, a
calculation, a default, two sums, and a count.
Figure 5:
Business Rules Paradigm Shift
To develop with business rules a paradigm shift from the
procedural to the declarative is required. Fortunately, it's an
easier shift than we made to the object-oriented world, and one that
business analysts quickly identify with.
After all, they're business
rules. Some of the advantages of a rules approach are:
- Business logic is not hidden in code: Business rules can be
quickly found and understood and thus speedily and reliably modified.
- Reduces errors: Business rules can be understood and
signed-off by business users with no implementation errors as the
system translates specifications into procedural code automatically.
The testing cycle is also significantly reduced.
- Naturally separates business logic from presentation logic:
J2EE architects can concentrate on infrastructure and integration,
not on the supervision of business logic development and maintenance.
- Enables new Java developers who know the business to be
quickly productive with J2EE technology.
Summary
The Model-View-Controller pattern continues to offer a
powerful abstraction to the J2EE architect, creating well defined but
loosely coupled components, enabling change. Although typically
applied at the presentation tier, the MVC pattern can also guide us
within the business tier, providing a clear delineation of work to
developers and infrastructure components. Through the discussion of
the Struts framework and the Versata Logic Suite for business logic
development we also saw how change is enabled through systems driven
from specifications, not code - moving toward what not how.
References
Date, C.J.(2000). What Not How: The Business Rules Approach
to Application Development. Addison-Wesley.
The original GUIDE paper and continuing work:
www.businessrulesgroup.org
von Halle, B. (2001). Building Business Rule Systems. John
Wiley & Sons, Inc. www.buildingbusinessrulesystems.com
Alur, D., Crupi, J., and Malks, D. (2001). Core J2EE
Patterns: Best Practices and Design Strategies. Prentice Hall.
Sucharitakul, A. (2001). "Seven Rules for Optimizing Entity
Beans." http://developer.java.sun.com/developer/technical
Articles/ebeans/sevenrules/. May.
Geary, D.M. (2001). Advanced JavaServer Pages. Prentice Hall.
Struts: http://jakarta.apache.org/struts
Versata, Inc.: www.versata.com
Author Bios
Steven Sweeting is director of product management with Versata, Inc.
Ever since he developed a C++ program to write most of his college
COBOL assignment, he's worked hard to avoid coding repetitive
business logic.
steven_sweeting@versata.com
Clive Jones is a senior enterprise Java developer with Online
Business Systems, a leading consulting company employing over 300
people throughout Canada and the U.S. He is based in Winnipeg,
Manitobo.
cjones@online-can.com
Aaron Rustad is a senior Java developer for Online Business Systems.
He specializes in architecting and developing enterprise applications
with a primary focus on Java and open-source technologies.
arustad@online-can.com
Listing 1
public UserForm extends ActionForm {
private String user = null;
private String address = null;
public getUser() { .. }
public setuser() { .. }
..
..
public ActionErrors validate (ActionMapping mapping,
HttpServletRequest req)
{
ActionErrors errors = new ActionErrors();
If (user == null || user.length() < 1)
{
Errors.add("user", new ActionError("error.user.required"));
}
..
..
return errors;
}
}
Listing 2
<!-- Importing the tag strut tag libraries -->
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
..
..
<html:form action="/SaveUserAction" focus="user">
<table border="0" width="100%">
<tr>
<th align="right">
<bean:message key="prompt.username"/>
</th>
<td align="left">
<html:text property="user" size="16" maxlength="16"/>
</td>
</tr>
<tr>
<th align="right">
<bean:message key="prompt.address"/>
</th>
<td align="left">
<html:text property="address" size="16" maxlength="16"/>
</td>
</tr>
<tr>
<td align="right">
<html:submit property="submit" value="Submit"/>
</td>
<td align="left">
<html:reset/>
</td>
</tr>
</table>
</html:form>
..
..
Listing 3
public final class SaveUserAction extends Action {
Public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest req,
HttpServletResponse res ) throws
IOException,
ServletException {
ActionForward forward = null;
UserForm userForm = (UserForm) form;
UserDelegate userDelegate = new UserDelegate();
try {
userDelegate( userForm );
} catch ( DelegateException e) {
forward = mapping.findForward("failure");
} finally {
if (forward == null) {
forward = mapping.findForward("success");
}
}
return forward;
}
Listing 4
<action-mapping>
<action
path="/SaveUser"
type="com.demo.SaveUserAction"
name="userForm"
scope="request"/>
<forward name="success" path="/saveSuccessful.jsp"/>
<forward name="failure" path="/saveFailed.jsp"/>
</action-mapping>
Listing 5
if ( childCascadeUpdate )
{
BigDecimal addValue = new BigDecimal("0");
BigDecimal subValue = new BigDecimal("0");
BigDecimal delta = new BigDecimal("0");
if ((this.getInvoiceStatus()).equals("B"))
{
addValue = getTotalFinalFee();
}
if ((this.getOldInvoiceStatus()).equals("B"))
{
subValue = getOldTotalFinalFee();
}
delta = addValue.subtract(subValue);
if ( ! delta.equals(new BigDecimal("0")) )
{
if ( !newParent.isInitialized() )
newParent.setDataObject(this.getAccount());
if ( ! newParent.isObjNull() )
newParent.getDataObject().setAdjust("TotalBilled",delta, true);
}
}
return;
}