Domain Payload Object

A coarse-grained object that transports whole domain objects between architectural layers in order to reduce the number of necessary method invocations.

domainpayloadobjectsummary

Background

When supporting the use of your Domain Model on the client tier we need a means to transport a payload of domain objects across architectural layers. The client will use an application service layer to request the domain objects it needs to use. The service will interact with the domain layer to read all necessary domain objects and then the service will map/assemble all of the domain objects into a simple container object called a Domain Payload Object. The payload object and its contained domain objects will then be returned to the requesting client.

By any name the Domain Payload Object is a specialized Data Transfer Object. The motivation for both patterns is the same. However, I have codified this as a separate pattern to emphasize the differences. A Data Transfer Object is a fine-grained object, providing properties that mirror or at least shadow properties found on the domain objects that they replicate. A Domain Payload Object is a coarse-grained object that transfers whole domain object instances to the client.

As noted in the Domain Dependency Resolver pattern, if you are using Remote Facade for your application services, you will have to determine if the Domain Dependency Resolver pattern and this one are right for your use. Key to using these patterns with Remote Facade is whether or not you can serialize domain objects that may have purposely unresolved lazy loaded properties. See Domain Dependency Resolver for tradeoffs to consider.

Working from Martin Fowler’s Data Transfer Object pattern example, the Album and Artist domain objects would occupy as few as one property in class AlbumPayload as my introductory class diagram shows. In that case the client would itself navigate into the Album instance to the Artist instance to get the name of the Artist. If you chose to you could map/assemble the AlbumPayloadobject with a separate artist property to reduce the necessary client navigation. But that is simply a design choice. The AlbumPayload still remains a coarse-grained transport object that is much easier to assemble, use, and maintain over the application lifecycle.

Of course my diagramed AlbumPayload would be of little use if it were to contain just one domain object. With only one domain object you wouldn’t bother using a payload container. My AlbumPayload also contains a collection of MusicType instances, which could be used in a use case or user story where the user edits an Album by associating it with one or more musical genres.

In many web applications the logical tiers are physically on just two or less (one!) physical tiers. In such cases all web, application, domain, and integration components will live in the same process, and typically the database will be in a separate process (but this is not always the case since some databases will run in-process with the web container). Therefore, it is much more efficient to simply move domain objects in bulk from one logical tier to another logical tier within the same physical process. It’s really just a matter of passing references to in-memory objects in the same process address space.

Even if the client tier and the service tier are physically separated by different processes, it is possible serialize whole domain objects across the wire, rather than to try to replicate large portions of domain objects into Data Transfer Objects and serialize them to the remote client. You will need to determine if this is true in your specific case if your client and application service layers are remote to one another.

If all logical tiers of concern to this pattern are in the same physical tier, why be concerned about using any sort of transport object? Why not employ a more finely-grained application service API and get individual objects on demand as desired, making multiple service invocations if necessary? The answer is, it’s mostly a matter of reducing the number transactions necessary to fulfill a use case flow or user story. Since transactions are commonly (and properly so) demarcated by application service methods, invoking more than one method on an application service will require multiple transactions. Database transactions are relatively expensive resources. Using the Domain Dependency Resolver pattern along with this one, all client domain object dependencies would be resolved within a single transaction. It makes perfect sense to reduce chattiness between client and application service tiers even when they are separated only logically.

Value and Benefits

You will provide a means for your clients to use the Domain Model directly. This will reduce the number of classes and complexity in designing, implementing, mapping/assembling, and maintaining a Data Transfer Object class hierarchy, since the specialized Domain Payload Objects are much easier to deal with in every way.

You will be able to support a coarse-grained application service layer API, even when providing fine- and medium-grained domain objects to your client. Chattiness is reduced between tiers, and transactions are kept to a bare minimum.

If you start out with all core application components on a single physical tier but are eventually required to enable a remote application service layer, it is possible to preserve your investment in payload objects. But as previously stated, that may take some additional thought and effort.

Putting It to Work

Create simple container objects with pairs of get/set methods for accessors. Create a mapper/assembler class for each payload object. Implement the each mapper to set whole domain objects into the payload container object. Return the payload object from the respective service method. When the client receives back control along with the payload object it can use the get methods to access the whole domain objects.

Requires Specification Strategy

Since the payload object mapper/assembler is a relatively trivial object to create and populate I do not codify it. Nonetheless I will comment on a simple approach for reusing Domain Payload Object classes.

Create general purpose payload object classes. Make them reusable across a set of similar use cases. Create one mapper for each general purpose payload object class. In your coarse-grained application service methods create a specification that informs the payload mapper which domain objects should be mapped. The mapper will map only the domain objects required according to the specification. The specification can be as simple as creating various constructors overloaded for each type of payload object configuration to build. Or you could use some sort of data structure to indicate which properties to map.

If you are using Java, one way to create a specification is using an enum and EnumSet. Create an enum with all the values you need to represent the various properties to be set by the mapper in the payload object. Pass an EnumSet to the mapper with each of the applicable values representing payload contents to be mapped from domain objects:

 

public class AlbumPayload {

	enum AlbumRequires {
		ALBUM,
		MUSIC_TYPES,
		. . .
	}

	private Album album;

	private Collection musicTypes;

	. . .
}

. . .

EnumSet tempRequiresSpec =
	EnumSet.of(AlbumRequires.ALBUM,  AlbumRequires.MUSIC_TYPE);

. . .

if (aRequiresSpec.contains(AlbumRequires.MUSIC_TYPE)) {
	// map in MusicType collection
}

The mapper now understands how to build a general purpose, reusable payload object with a variety of different configurations.

Consequences

Using the Domain Payload Object pattern is a simplified and specialized type of Data Transfer Object. But there are important differences and tradeoffs.

  • Contain Whole Domain Objects: The point of this pattern is to create a simple, reusable object container to house whole domain objects. If you slip into mapping domain entity properties to payload object properties, you are really creating Data Transfer Objects.
  • Use Caution With Remote Facade: Take special note if you use both Domain Dependency Resolver and Remote Facade. Serializing domain object entities that have lingering lazy loading proxies—because a specific client didn’t require them to load real entity instances from persistence store—may be either impossible or impractical. You will have to either come up with a way of eliminating the proxy objects or find a way to serialize them, which may not be possible. See pattern Domain Dependency Resolver.
  • Reusability Strengthened Using Requires Specifications: Create payload objects for specific use case flows or user stories by generalizing the payload classes themselves, but specializing the mapping of each us a Requires Specification Strategy (see above). If you specialize your payload classes instead, you will have more classes that only differ slightly in their contents.

Related Patterns

The following are patterns that may be used in conjunction with the Domain Payload Object:

  • Domain Dependency Resolver: This pattern is meant to be used specifically in situations where you use a true Domain Model, you use Domain Dependency Resolver, and your client and application layers are on the same physical tier. As clearly indicated all these patterns can be used in physically separated tiers, but more work is involved in doing so.