Context
In a Java 2 Platform,
Enterprise Edition (J2EE) application, the server-side business
components are implemented using session beans, entity beans,
DAOs, and so forth. Application clients
frequently need to access data that is composed from multiple
objects.
Problem
Application clients
typically require the data for the model or parts of the model to
present to the user or to use for an intermediate processing step
before providing some service. The application model is an
abstraction of the business data and business logic implemented on
the server side as business components. A model may be expressed as
a collection of objects put together in a structured manner (tree or
graph). In a J2EE application, the model is a distributed collection
of objects such as session beans, entity beans, or
DAOs and other objects. For a client to
obtain the data for the model, such as to display to the user or to
perform some processing, it must access individually each
distributed object that defines the model. This approach has several
drawbacks:
-
Because the client must access
each distributed component individually, there is a tight
coupling between the client and the distributed components of
the model over the network
-
The client accesses the
distributed components via the network layer, and this can lead
to performance degradation if the model is complex with numerous
distributed components. Network and client performance
degradation occur when a number of distributed business
components implement the application model and the client
directly interacts with these components to obtain model data
from that component. Each such access results in a remote method
call that introduces network overhead and increases the
chattiness between the client and the business tier.
-
The client must reconstruct the
model after obtaining the model's parts from the distributed
components. The client therefore needs to have the necessary
business logic to construct the model. If the model construction
is complex and numerous objects are involved in its definition,
then there may be an additional performance overhead on the
client due to the construction process. In addition, the client
must contain the business logic to manage the relationships
between the components, which results in a more complex, larger
client. When the client constructs the application model, the
construction happens on the client side. Complex model
construction can result in a significant performance overhead on
the client side for clients with limited resources.
Because the client is
tightly coupled to the model, changes to the model require changes
to the client. Furthermore, if there are different types of clients,
it is more difficult to manage the changes across all client types.
When there is tight coupling between the client and model
implementation, which occurs when the client has direct knowledge of
the model and manages the business component relationships, then
changes to the model necessitate changes to the client. There is the
further problem of code duplication for model access, which occurs
when an application has many types of clients. This duplication
makes client (code) management difficult when the model changes.
Forces
-
Separation of business logic is
required between the client and the server-side components.
-
Because the model consists of
distributed components, access to each component is associated
with a network overhead. It is desirable to minimize the number
of remote method calls over the network.
-
The client typically needs only to
obtain the model to present it to the user. If the client must
interact with multiple components to construct the model on the
fly, the chattiness between the client and the application
increases. Such chattiness may reduce the network performance.
-
Even if the client wants to
perform an update, it usually updates only certain parts of the
model and not the entire model.
-
Clients do not need to be aware of
the intricacies and dependencies in the model implementation. It
is desirable to have loose coupling between the clients and the
business components that implement the application model.
Clients do not otherwise
need to have the additional business logic required to construct the
model from various business components.
Solution
Use a Transfer Object Assembler to build the required model or
submodel. The Transfer Object Assembler
uses Transfer Objects to retrieve data from various business objects
and other objects that define the model or part of the model.
The Transfer Object
Assember constructs a composite Transfer
Object that represents data from different business components. The
Transfer Object caries the data for the model to the client in a
single method call. Since the model data can be complex, it is
recommended that this Transfer Object be immutable. That is, the
client obtains such Transfer Objects with the sole purpose of using
them for presentation and processing in a read-only manner. Clients
are not allowed to make changes to the Transfer Objects.
When the client needs the
model data, and if the model is represented by a single
coarse-grained component (such as a Composite Entity), then the
process of obtaining the model data is simple. The client simply
requests the coarse-grained component for its composite Transfer
Object. However, most real-world applications have a model composed
of a combination of many coarse-grained and fine-grained components.
In this case, the client must interact with numerous such business
components to obtain all the data necessary to represent the model.
The immediate drawbacks of this approach can be seen in that the
clients become tightly coupled to the model implementation (model
elements) and that the clients tend to make numerous remote method
invocations to obtain the data from each individual component.
In some cases, a single
coarse-grained component provides the model or parts of the model as
a single Transfer Object (simple or composite). However, when
multiple components represent the model, a single Transfer Object
(simple or composite) may not represent the entire model. To
represent the model, it is necessary to obtain Transfer Objects from
various components and assemble them into a new composite Transfer
Object. The server, not the client, should perform such "on-the-fly"
construction of the model.
Structure
Figure 8.27 shows the class
diagram representing the relationships for the Transfer Object
Assembler pattern.
Figure 8.27 Transfer Object Assembler
class diagram
Participants and Responsibilities
The sequence diagram in
Figure 8.28 shows the interaction between the various participants
in the Transfer Object Assembler pattern.
Figure 8.28 Transfer Object Assembler
sequence diagram
TransferObjectAssembler
The
TransferObjectAssembler is the main class of this pattern.
The TransferObjectAssembler constructs a
new Transfer Object based on the requirements of the application
when the client requests a composite Transfer Object. The
TransferObjectAssembler then locates the
required BusinessObject instances to
retrieve data to build the composite Transfer Object.
BusinessObjects are business-tier
components such as entity beans and session beans,
DAOs, and so forth.
Client
If the
TransferObjectAssembler is implemented as an arbitrary Java
object, then the client is typically a Session Facade that provides
the controller layer to the business tier. If the
TransferObjectAssembler is implemented
as a session bean, then the client can be a Session Facade or a
Business Delegate.
BusinessObject
The
BusinessObject participates in the construction of the new
Transfer Object by providing the required data to the
TransferObjectAssembler. Therefore, the
BusinessObject is a role that can be
fulfilled by a session bean, an entity bean, a DAO, or a regular
Java object.
TransferObject
The
TransferObject is a composite Transfer Object that is
constructed by the TransferObjectAssembler
and returned to the client. This represents the complex data from
various components that define the application model.
BusinessObject
BusinessObject is a role that can be fulfilled by a session
bean, entity bean, or DAO. When the assembler needs to obtain data
directly from the persistent storage to build the Transfer Object,
it can use a DAO. This is shown as the
DataAccessObject object in the diagrams.
Strategies
This section explains
different strategies for implementing a Transfer Object Assembler
pattern.
Java
Object Strategy
The
TransferObjectAssembler can be an arbitrary Java object and
need not be an enterprise bean. In such implementations, a session
bean usually fronts the TransferObjectAssembler.
This session bean is typically a Session Facade that performs its
other duties related to providing business services. The
TransferObjectAssembler runs in the
business tier, regardless of the implementation strategies. The
motivation for this is to prevent the remote invocations from the
TransferObjectAssembler to the source
objects from crossing the tier.
Session Bean Strategy
This strategy implements
the TransferObjectAssembler as a session
bean (as shown in the class diagram). If a session bean
implementation is preferred to provide the
TransferObjectAssembler as a business service, it is
typically implemented as a stateless session bean. The business
components that make up the application model are constantly
involved in transactions with various clients. As a result, when a
TransferObjectAssembler constructs a new
composite Transfer Object from various business components, it
produces a snapshot of the model at the time of construction. The
model could change immediately thereafter if another client changes
one or more business components, effectively changing the business
application model.
Therefore, implementing
TransferObjectAssembler as a
stateful session bean provides no
benefits over implementing it as a stateless session bean, as
preserving the state of the composite model data value when the
underlying model is changing is futile. If the underlying model
changes, it causes the Transfer Object held by the assembler to
become stale. The TransferObjectAssembler,
when next asked for the Transfer Object, either returns a stale
state or reconstructs the Transfer Object to obtain the most recent
snapshot. Therefore, it is recommended that the assembler be a
stateless session bean to leverage the benefits of stateless over
stateful session beans.
However, if the underlying
model rarely changes, then the assembler may be a
stateful session bean and retain the
newly constructed Transfer Object. In this case, the
TransferObjectAssembler must include
mechanisms to recognize changes to the underlying model and to
reconstruct the model for the next client request.
Business Object Strategy
The
BusinessObject role in this pattern can be supported by
different types of objects, as explained below.
-
The
BusinessObject can be a session bean. The Transfer Object
Assembler may use a Service Locator (see "Service Locator" on
page 368) to locate the required session bean. The Transfer
Object Assembler requests this session bean to provide the data
to construct the composite Transfer Object.
-
The
BusinessObject can be an entity bean. The Transfer Object
Assembler may use a Service Locator to locate the required
entity bean. The Transfer Object Assembler requests this entity
bean to provide the data to construct the composite Transfer
Object.
-
The
BusinessObject can be a DAO. The Transfer Object
Assembler requests this DAO to provide the data to construct the
composite Transfer Object.
-
The
BusinessObject can be an arbitrary Java object. The
Transfer Object Assembler requests this Java object to provide
the data to construct the composite Transfer Object.
The
BusinessObject can be another Transfer Object Assembler. The
first Transfer Object Assembler requests the second Transfer Object
Assembler to provide the data to construct the composite Transfer
Object.
Consequences
-
Separates Business Logic
When the client includes logic to manage the interactions with
distributed components, it becomes difficult to clearly separate
business logic from the client tier. The Transfer Object
Assembler contains the business logic to maintain the object
relationships and to construct the composite Transfer Object
representing the model. The client needs no knowledge of how to
construct the model or the different components that provide
data to assemble the model.
-
Reduces Coupling
Between Clients and the Application
Model
The Transfer Object Assembler hides the complexity of the
construction of model data from the clients and establishes a
loose coupling between clients and the model. With loose
coupling, if the model changes, then the Transfer Object
Assembler requires a corresponding change. However, the client
is not dependent on the model construction and
interrelationships between model business components, so model
changes do not directly affect the client. In general, loose
coupling is preferred to tight coupling.
-
Improves Network Performance
The Transfer Object Assembler drastically reduces the network
overhead of remote method calls and chattiness. The client can
request the data for the application model from the Transfer
Object Assembler in a single remote method call. The assembler
constructs and returns the composite Transfer Object for the
model. However, the composite Transfer Object may contain a
large amount of data. Thus, while use of the Transfer Object
Assembler reduces the number of network calls, there is an
increase in the amount of data transported in a single call.
This trade-off should be considered in applying this pattern.
-
Improves Client Performance
The server-side Transfer Object
Assembler constructs the model as a composite Transfer Object
without using any client resources. The client spends no time
assembling the model.
-
Improves Transaction Performance
Typically, updates are isolated to a
very small part of the model and can be performed by
fine-grained transactions. These transactions focus on isolated
parts of the model instead of locking up the coarse-grained
object (model). After the client obtains the model and displays
or processes it locally, the user (or the client) may need to
update or otherwise modify the model. The client can interact
directly with a Session Facade to accomplish this at a suitable
granularity level. The Transfer Object Assembler is not involved
in the transaction to update or modify the model. There is
better performance control because transactional work with the
model happens at the appropriate level of granularity.
-
May Introduce Stale Transfer
Objects
The Transfer Object Assembler constructs
Transfer Objects on demand. These Transfer Objects are snapshots
of the current state of the model, represented by various
business components. Once the client obtains a Transfer Object
from the assembler, that Transfer Object is entirely local to
the client. Since the Transfer Objects are not network-aware,
other changes made to the business components used to construct
the Transfer Object are not reflected in the Transfer Objects.
Therefore, after the Transfer Object is obtained, it can quickly
become stale if there are transactions on the business
components.
Sample Code
Implementing the Transfer Object Assembler
Consider a Project
Management application where a number of business-tier components
define the complex model. Suppose a client wants to obtain the model
data composed of data from various business objects, such as:
-
Project Information from the
Project component
-
Project Manager information from
the ProjectManager component
-
List of Project Tasks from the
Project component
Resource Information from
the Resource component
A composite Transfer Object
to contain this data can be defined as shown in Example 8.24. A
Transfer Object Assembler pattern can be implemented to assemble
this composite Transfer Object. The Transfer Object Assembler sample
code is listed in Example 8.28.
Example 8.24 Composite Transfer Object Class
public class
ProjectDetailsData {
public
ProjectTO projectData;
public
ProjectManagerTO
projectManagerData;
public Collection
listOfTasks;
...
} |
The list of tasks in the
ProjectDetailsData is a collection of
TaskResourceTO objects. The
TaskResourceTO is a combination of
TaskTO and
ResourceTO. These classes are shown in Example 8.25, Example
8.26, and Example 8.27.
Example 8.25 TaskResourceTO Class
public class
TaskResourceTO {
public String
projectId;
public String
taskId;
public String name;
public String description;
public Date
startDate;
public Date
endDate;
public
ResourceTO assignedResource;
...
public
TaskResourceTO(String projectId,
String
taskId, String name, String description,
Date
startDate, Date endDate,
ResourceTO
assignedResource) {
this.projectId = projectId;
this.taskId = taskId;
...
this.assignedResource =
assignedResource;
}
...
} |
Example 8.26
TaskTO Class
public class
TaskTO {
public String
projectId;
public String
taskId;
public String name;
public String description;
public Date
startDate;
public Date
endDate;
public
assignedResourceId;
public
TaskTO(String projectId,
String taskId,
String name, String
description, Date startDate,
Date
endDate, String
assignedResourceId) {
this.projectId = projectId;
this.taskId = taskId;
...
this.assignedResource =
assignedResource;
}
...
} |
Example 8.27
ResourceTO Class
public class
ResourceTO {
public String
resourceId;
public String
resourceName;
public String
resourceEmail;
...
public
ResourceTO (String resourceId,
String
resourceName, String
resourceEmail, ...) {
this.resourceId = resourceId;
this.resourceName = resourceName;
this.resourceEmail =
resourceEmail;
...
}
} |
The
ProjectDetailsAssembler class that assembles the
ProjectDetailsData object is listed in
Example 8.28.
Example 8.28 Implementing the Transfer Object Assembler
public class
ProjectDetailsAssembler
implements
javax.ejb.SessionBean {
...
public
ProjectDetailsData getData(String
projectId){
// Construct the composite
Transfer Object
ProjectDetailsData pData
= new
ProjectDetailsData();
//get the project details;
ProjectHome projectHome =
ServiceLocator.getInstance().getHome(
"Project",
ProjectEntityHome.class);
ProjectEntity project =
projectHome.findByPrimaryKey(projectId);
ProjectTO projTO =
project.getData();
// Add Project Info to
ProjectDetailsData
pData.projectData = projTO;
//get the project manager
details;
ProjectManagerHome
projectManagerHome =
ServiceLocator.getInstance().getHome(
"ProjectManager",
ProjectEntityHome.class);
ProjectManagerEntity
projectManager =
projectManagerHome.findByPrimaryKey(
projTO.managerId);
ProjectManagerTO projMgrTO
=
projectManager.getData();
// Add
ProjectManager info to
ProjectDetailsData
pData.projectManagerData =
projMgrTO;
// Get list of
TaskTOs from the Project
Collection
projTaskList =
project.getTasksList();
// construct a list of
TaskResourceTOs
ArrayList
listOfTasks = new ArrayList();
Iterator
taskIter =
projTaskList.iterator();
while (taskIter.hasNext())
{
TaskTO task = (TaskTO)
taskIter.next();
//get the Resource
details;
ResourceHome resourceHome
=
ServiceLocator.getInstance().getHome(
"Resource", ResourceEntityHome.class);
ResourceEntity resource =
resourceHome.findByPrimaryKey(
task.assignedResourceId);
ResourceTO resTO =
resource.getResourceData();
// construct a new
TaskResourceTO using Task
// and Resource data
TaskResourceTO trTO = new
TaskResourceTO(
task.projectId,
task.taskId,
task.name,
task.description,
task.startDate,
task.endDate,
resTO);
// add
TaskResourceTO to the list
listOfTasks.add(trTO);
}
// add list of tasks to
ProjectDetailsData
pData.listOfTasks = listOfTasks;
// add any other data to
the Transfer Object
...
// return the composite
Transfer Object
return
pData;
}
...
} |
Related Patterns
-
Transfer Object
The Transfer Object Assembler uses the Transfer Object pattern
in order to create and transport Transfer Objects to the client.
The Transfer Objects created carry the data representing the
application model from the business tier to the clients
requesting the data.
-
Composite Entity
The Composite Entity pattern promotes a coarse-grained entity
bean design, where entities can produce composite Transfer
Objects similar to the one produced by the Transfer Object
Assembler. However, the Transfer Object Assembler is more
applicable when the composite Transfer Object constructed is
derived from a number of components (session beans, entity
beans, DAOs, and so forth), whereas
the Composite Entity pattern constructs the Transfer Object from
its own data (that is, a single entity bean).
-
Session Facade
The Transfer Object Assembler is typically implemented as a
stateless session bean. As such, it could be viewed as a limited
special application of the Session Facade pattern. More
importantly, Transfer Object Assembler constructs composite
Transfer Objects that are immutable. Therefore, the client
receiving this composite Transfer Object can only use the data
for its presentation and processing purposes. The client cannot
update the Transfer Object. If the client needs to update the
business objects that derive the composite Transfer Object, it
may have to access the Session Facade (session bean) that
provides that business service.
-
Data Access Object
A possible strategy for the Transfer
Object Assembler involves obtaining data for the composite
Transfer Object from the persistent store without enterprise
bean involvement. The Data Access Object pattern can be applied,
thus leveraging its benefits to provide persistent storage
access to the Transfer Object Assembler.
-
Service Locator
The Transfer Object Assembler needs to locate and use various
business objects. The Service Locator pattern can be used in
conjunction with the Transfer Object Assembler pattern whenever
a business object or a service needs to be located.
Contact Us© 2001-2002
Sun Microsystems, Inc. All Rights Reserved.
|