Pattern: Parameter Tree

How can containment relationships be expressed when defining complex representation elements and exchanging such related elements at runtime?


The final version of this pattern is featured in our book Patterns for API Design: Simplifying Integration with Loosely Coupled Message Exchanges.

Pattern: Parameter Tree

also known as: Single Complex Representation, Tree Representation, Bar

Context

An API provider wants to offer one or more Operations to API clients using an API endpoint. To enable the communication, they need to agree on the structure of each message (i.e, the request message and response message of the Operation) to be exchanged.

Problem

How can containment relationships be expressed when defining complex representation elements and exchanging such related elements at runtime?

Forces

The data structures of the request messages and response messages are an essential part of the API contract. The following forces (shared with Atomic Parameter, Atomic Parameter List, and Parameter Forest) need to be balanced:

  • Structure of the domain model and the system behavior and its impact on understandability (including considerations of simplicity, complexity, and traceability)
  • Additional data to be transmitted (such as security-related data or metadata)
  • Performance (latency, message processing) and resource use (bandwidth, memory, CPU power)
  • Loose coupling and interoperability
  • Developer convenience and experience
  • Security and data privacy

Pattern forces are explained in depth in the book.

Solution

Define a Parameter Tree as a hierarchical structure with a dedicated root node that has one or more child nodes. Each child node may be a single Atomic Parameter, an Atomic Parameter List, or another Parameter Tree, identified locally by a name and/or by position. Each node might have an exactly-one cardinality, but also a zero-or-one cardinality, an at-least-one cardinality, or a zero-or-more cardinality.

Sketch

A solution sketch for this pattern from pre-book times is:

Figure 1: Parameter Tree pattern with two variations, nested list and homogeneous, flat collection (iconic visualization)

Variants

Parameter Collection. If the Parameter Tree has a nesting level of 1 and all children of the root element have the same structure/type, the tree morphs into a list and is also called Parameter Collection. This variant models JSON arrays and XML elements with maxoccur values greater than one in our pattern language.

Atomic Parameter Collection. If all tree nodes on level 1 are leaves (i.e., instances of the Atomic Parameter pattern), the resulting Data Transfer Representation (DTR) structure and pattern variant is actually an Atomic Parameter List embedded in the tree that can also be called an Atomic Parameter Collection.

Example

Our JAX-RS example available at GitHub uses an instance of this pattern when returning updated claims in response to HTTP PUT requests:

public class ClaimDTO {
    private final UUID id;
    private final String dateOfIncident;
    private final double amount;
    private final List<Evidence> evidence;
    [...]
}

@PUT
@Path("/{claimId}")
public ClaimDTO updateClaim(@PathParam("claimId") UUID claimId,
                            @NotNull @Valid Claim claim) {
    boolean result = claims.update(claim);
    if (!result) {
        throw noSuchClaim.get();
    }
    return ClaimDTO.create(claim);
}

A sample GET request for a claim is:

GET http://localhost:8000/claims/0afeb849-6d63-40b6-b52f-21dee16fdda5

This would yield an instance of the ClaimDTO returned to the API client that looks like this:

{"claim":
    {"id":"0afeb849-6d63-40b6-b52f-21dee16fdda5",
    "dateOfIncident":"2017-02-14",
    "amount":2000.0,
    "evidence":[],
    "links":[{"uri":"http://localhost:8080/claims/0afeb849-6d63-40b6-b52f-21dee16fdda5",
            "params":{"rel":"self"},
            "type":null,
            "rel":"self",
            "uriBuilder":{"absolute":true},
            "rels":["self"],
            "title":null}]}}

The ClaimDTO with its attributes constitutes the Parameter Tree in the example. Two of these attributes (evidence and links) apply the Parameter Tree pattern again. The resulting Parameter Tree is marshalled into a JSON object that contains an array (which is empty for evidence in the above JSON/HTTP snippet).

Implementation hints

When applying and realizing this pattern, the following advice should be taken into consideration:

  • Follow the common hints and pitfalls on robustness of API contracts and validation of messages as explained in Section. In this pattern, this is substantially harder than in Atomic Parameter and Atomic Parameter List as specification of upper and lower boundaries, considerations about optionality and Null values, and validation measures need to be done for each Atomic Parameter, as well as each list structure (arrays, sequences) in the tree.
  • Resist the temptation to represent the real world exactly and completely (with all variations and exceptions modeled explicitly) to minimize message verbosity; the general “if in doubt, leave it out” rule for modeling (and other specification efforts) also applies to data modeling and interface representation design.
  • Just like for all nontrivial data structures, provide sample data and test cases, but also machine-readable specifications such as schemas (e.g., using JSON Schema or XML Schema) to support regression testing and long-term maintenance.
  • Consider using compression (as for instance supported in HTTP2).
  • Avoid overly deep nesting of data structures unless minimizing the number of calls has high priority (e.g., in a mobile applications) and the API client is expected to follow domain model links to reference data anyway (e.g., a customer is referenced in a contract or a purchase, and customer details have to be displayed).
  • Be reluctant to introduce fully generic data structures (e.g., key-value pairs to be built and interpreted dynamically); the promised flexibility might backfire and cause difficulties in interface comprehension by the developers that lead to additional debugging and testing efforts in the long run. Flexibility comes at a price; domain-specific abstractions and names make tools such as code completion and test automation more powerful.
  • When dealing with an Object-Oriented (OO) domain model in the service implementation (backend), consider using an OO-to-XML mapper (but conduct a thorough proof-of-technology before deciding for one strategically).

While technically possible and [sometimes even suggested] (http://dret.typepad.com/dretblog/2007/10/http-get-with-m.html), a Parameter Tree contained in the request message of an HTTP GET request is not supposed to have any effect on the provider and should therefore be avoided according to the HTTP/1.1 specification (see a related Stack Overflow discussion).

Consequences

The resolution of pattern forces and other consequences are discussed in our book.

Known Uses

When JAX-RS is used to implement a message-based remote API, @Consumes and @Produces annotations that refer to custom media types (e.g., nested JSON objects with a single root) may indicate instances of this pattern.1

The JIRA Cloud REST APIs use this pattern in the requests of its issue-createIssue message; note that it uses an Atomic Parameter List for the corresponding responses. The JIRA Cloud REST API also uses Pagination.

The API call GET collections in the Twitter REST API contains a single nested JSON object called objects with subordinates that list users, timelines, etc.

The messages created with the Protocol Buffers data interchange format (originally developed by Google and open sourced at GitHub) can also be seen as instances of this pattern. The same holds true for the complex types in Apache Avro, which are serialized into JSON.

More Information

Related Patterns

Sending multiple messages as Atomic Parameter or Atomic Parameter List is an alternative to Parameter Tree. If the structure gets too complex or it is awkward to group the data under a single root, Parameter Forest can be considered as an alternative for complex parameter representations. Atomic Parameter is a building block of Parameter Tree (for leafs of the tree). Atomic Parameter List can use a Parameter Tree as a wrapper structure for transport, if the technology used supports no other way to transport multiple parameters. Parameter Forest contains a list built from elements based on the other three patterns: Atomic Parameter, Parameter Tree, and Atomic Parameter List.

A Parameter Tree or a Parameter Forest is usually used for the data structuring in the response messages (i.e., the pages) of Pagination.

Like Atomic Parameter and Atomic Parameter List this pattern can be used in the three patterns Command Message, Document Message, and Event Message from Hohpe and Woolf (2003), if the content can be represented as a tree. For instance, a command with complex parameters or an event that is described in a complex structure can be represented as a tree. A Document Message is often transported as a Parameter Tree.

Other Sources

As described in Hohpe and Woolf (2003), often messages originating from external systems contain many levels of nested, repeating data elements as they are modeled after generic, normalized database structures. The resulting message structure is than often a Parameter Tree or Parameter Forest. Content Filters Hohpe and Woolf (2003) can be used to simplify such structures, e.g., into a list, if not all the information is needed. Some Content Enrichers Hohpe and Woolf (2003) in turn retrieve data from such external systems and enrich messages with such data; hence, a Parameter Tree or a Parameter Forest is often the resulting data structure.

References

Hohpe, Gregor, and Bobby Woolf. 2003. Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley.


  1. See this online tutorial for examples: http://www.mkyong.com/webservices/jax-rs/integrate-jackson-with-resteasy/. Note that the tutorial does not feature all platform-specific design guidelines and recommended practices for REST (e.g., the URI should not include an action code in verb form because this action code is already given by the HTTP methods such as GET and POST).↩︎