Hypermedia Continuum = (PO[X|JO]->Data->Links->Hypermedia)

2010-09-30 @ 11:55#

When I talk to developers about the advantages of using Hypermedia types in your distributed network apps, I usually get lots of positive feedback. But almost as often I hear from devs that they are not really sure how to get started; how to move from their current "type-less" implemenation to a "hypermedia-typed" one. In this post, I describe possible path for shops that want to move toward a more hypermedia-oriented experience.

WARNING: There be dragons ahead.
Don't go here unless you are sure you'd rather 'roll your own' [hyper]media type instead of using an existing, properly registered one such as [X]HTML or Atom.

Creating a custom media-type for internal use does not need to be very complicated. If you are working on a localized application and want to get everyone using the same state-transfer contract, you can use a simple media type that has just a few 'hooks' and allows for future expansion as your needs grow. You can use this handy set of examples to orchestrate your move as far along the "hypermedia continuum" as you wish.

POX and POJO : The Object Container

An easy start is to adopt a simple data format that takes on properties of both [X]HTML and Atom. The <body /> portion can hold a serialization of a custom object (ala Atom-style) at the beginning. This makes it easy for developers to use existing frameworks when crafting messages. This can be done using most existing frameworks that can convert internal objects into XML and/or JSON serializations. Here's a an example 'skeleton' in XML:

 
<root>
  <meta>
    <!-- meta-data, system info, etc. can go here -->
  </meta>
  <body>
    <user>
      <email></email>
      <username></username>
      <date-created></date-created>
    </user>
  </body>
  <error>
    <!-- place any error details (text, stack trace, etc.) here -->
  </error>
</root>

Data : The Standardized Data Representation

The next step is to standardize the representation of data in the <body /> section of the message. IOW, eliminate the use of the object serialization pattern and use an agreed data format that is not bound to server-side storage|object-types. One simple format is to adopt a generic flat name-value pair (NVP) pattern:

 
<root>
  <meta>
    <!-- meta-data, system info, etc. can go here -->
  </meta>
  <body>
    <item name="user">
      <data name="email"></data>
      <data name="username"></data>
      <data name="date-created"></data>
    </item name="user">
  </body>
  <error>
    <!-- place any error details (text, stack trace, etc.) here -->
  </error>
</root>

Using a dedicated data format like this will add some work for developers; they will need to drop the habit of blindly serializing internal objects as public messages. However, once a simple conversion utility is built, it is relatively easy to read/write a data format like this. Even better, if you need to add new elements, they can appear as one more in the collection. This makes evolving the messages trivial.

Links : Unbinding the Client

In the previous example, any client that understands the data format can understand the contents of the message. However, "any client" cannot determine which items are writeable, which URIs to use when reading or writing data, etc. In other words, clients are still "bound" to the Read/Write rules (semantics) established ahead of time in documentation. Any change in these rules (new URIs, different elements to read/write) will require a change in the client.

The first step in creating an environment that allows servers to make changes in the details of the message w/o requiring re-coding clients is to pull the actual URIs (links) out of the written documentation and insert them directly in the messages themselves. Here's a simple example:

 
<root href="...">
  <meta>
    <!-- meta-data, system info, etc. can go here -->
  </meta>
  <body>
    <item name="user" href="...">
      <data name="email"></data>
      <data name="username"></data>
      <data name="date-created"></data>
    </item name="user">
  </body>
  <error>
    <!-- place any error details (text, stack trace, etc.) here -->
  </error>
</root>

By sending the actual URIs in the messages, clients can be "trained" to locate and use the URIs to read and write data. This means the server can change URIs at any time w/o requiring the client to be updated or re-coded. This is the first major step toward creating a [hyper]media type that generic clients can use to read and write data to any server that uses that hypermedia type.

Hypermedia : App Controls

In the previous examples, all hypermedia information must be encoded in the client and server themselves. For example, knowing what data to write, which HTTP method to use when writing, etc. However, you can greatly increase the value of your data format by adding this semantic information into the messages themselves. This takes more planning and requires additional coding (mostly for clients), but can pay off in the long run. Here's a simple example:

 
<root>
  <meta>
    <!-- meta data, system info, etc. can go here -->
  </meta>
  <body>
    <!-- allow searching for one or more users using HTTP GET -->
    <query href="..." rel="search">
      <data name="username" />
    </query>
    <!-- allow writing data : if rel contains "update", use HTTP PUT -->
    <write href="..." rel="user update">
      <data name="email"></data>
      <data name="username"></data>
      <data name="date-created"></data>
    </write>
    <!-- if rel contains "add" use HTTP POST -->
    <write href="..." rel="user add">
      <data name="email"></data>
      <data name="username"></data>
      <data name="date-created"></data>
    </write>
  </body>
  <error>
    <!-- place any error details (text, stack trace, etc.) here -->
  </error>
</root>

Using Hypermedia controls in the message means clients need to be coded to search for and understand the various pre-defined controls (in this example they are "query" and "write"). Although this takes additional work at first, it means future messages can be crafted w/ new or changed semantics w/o requiring a rewrite of the client code.

Clients that understand the semantics within a message (via the application controls) will be able to understand and properly handle entirely new messages (not just "users" but "customers", "orders", etc.) without requiring re-coding. Not only will the same client be able to read new data, but also write data to the server based on the details sent in the message. All without the need for re-writing and re-deploying the client code.

Going Beyond Simple

This is example Hypermedia format, while useful, is very simple. There are lots of features that could be added, maybe required to make a data format into a useful media type. The point is that, starting this way means you can create a focus on using media-types as a fundamental aspect of your overall architecture. By doing so, you get a whole bunch of 'win' without much upfront cost.

Hypermedia