Exchanging Data in Distributed apps

We've previously noted how much more complex distributed apps are, in comparison to collocated apps. This complexity also affects data access. However we implement the nuts and bolts of persistence in a distributed app, we're going to need to send data back to the remote client. This data needs to be disconnected from the backing data store. Of course, we could send the client remote references to entity beans, but that guarantees dreadful performance and scalability.

The Value Object J2EE Pattern

A value object is a serializable Java object that can be passed across the network efficiently and contains the data necessary in the context of the current use case. Value objects are usually JavaBeans on which the server invokes setter methods and the client getter methods. However, a client might modify the state of a value object and pass it back to the server to update persistent data. There are many possible refinements; for example, the client may be presented with an object having an immutable interface, while the server implementation adds getter methods.


Value objects are sometimes referred to as Data Transfer Objects (DTOs).

The Value Object J2EE pattern describes the use of value objects to avoid chatty remote calling. In EJB 1.1, this was commonly used between session beans and entity beans. EJB 1.1 entity beans needed methods to perform bulk data retrieval and update, so that session beans could update data in a single remote call, rather than by invoking individual setter methods. As EJB 2.0 entities should be given local interfaces, there is no longer any need for entity beans to implement methods that work with value objects. This is a good thing, as value objects are artifacts of a business process, not data storage. One entity may in fact require multiple value objects in different circumstances. (Although the following discussion uses an entity bean as an example, the issues it raises are common to many persistence strategies.) Consider a Car entity (not a realistic attempt at modeling!) containing the following data: year of manufacture, model name, manufacturer (one-to-one relationship), color, registration plate, and the car's owners (one-to-many relationship). In our simple model, each owner may only own one car, and must have one associated insurance policy object. Each insurance policy object is associated with one insurer. The following UML class diagram illustrates these relationships among persistent objects:

Java Click To expand

No single Car value object will be universally useful: it will contain too litde or too much data. For example, if we take just the information held directly in the Car entity, we're left with year, model name, color, and registration plate. This may be useful in some cases, but what if we're interested in the car's ownership history? If we decide that our single value object should traverse relationships, we are left with the problem of where to stop, or will end up performing too many lookups and sending back too much data, seriously reducing performance (for example, should we stop at a Car's associated Owner objects, or should we go on to materialize the transitive closure, including InsurancePolicy and Insurer objects?). The best, but fairly complex, solution is to use multiple value objects: for example, we might have a CarOwnership value object, containing the primary key of the car and an array of value objects holding information about its owners. Collocated apps do not usually face this problem, as diere's no need to "disconnect" data. A component that creates value objects in response to business need implements the Value Object Assembler pattern (Core J2EE Patterns). Value object assemblers may be session beans or persistence façades.


Create value objects as needed to support use cases. Don't automatically use one value object per entity bean or RDBMS table (although there are times when this is appropriate).

"Generic" Value Objects

The Value Object pattern requires a lot of effort to implement. We may need multiple value objects per persistent object, and will need to write custom code to create and return value objects. It's often possible to auto-generate a value object for an entity bean or other mapped object, but this will produce a "one size fits all" value object which, as we've seen, won't satisfy all requirements. Especially when many value objects are necessary for one persistent object, a HashMap can be considered as an alternative to send data to the client. This doesn't have the advantage of strong typing, but may be worthwhile if it eliminates excessive amounts of code in value objects.

Another untyped approach is to use XML documents as generic value objects. However, we concluded in that this approach had serious disadvantages that make it inappropriate in most cases.

"Disconnected" Data Access Using JDBC Rowsets

Another alternative to the Value Object pattern uses standard JDBC infrastructure. The javax.sql.RowSet interface is a subinterface of the java.sql.ResultSet interface that can be disconnected from the database. It's a resultset that is also a set of value objects. EJB Design Patterns suggests using rowsets to marshal read-only data back to the client in the so-called Data Transfer Rowset J2EE pattern. This is efficient and generic, with the potential to save code in custom value objects. However, I don't feel it deserves to be regarded as a pattern, and don't recommend it. There are serious drawbacks to using JDBC rowsets in this way from an architectural perspective:

If the number of value objects in a distributed app is becoming problematic, sending hashmaps back to the client is preferable to using rowsets, and not much more difficult to implement However, the hashmap approach is little help when we face complex issues of how far to traverse associations. For example, we can conditionally include associated fields of an object in a hashmap, but will strike problems if we want to traverse the associations of these fields only to a certain depth. We will normally need to resort to coding custom value objects in such cases, and may end up needing to implement and maintain a large number of value objects.