Chapter 5. Hypermedia Services

EMBRACING HTTP AS AN APPLICATION PROTOCOL puts the Web at the heart of distributed systems development. But that’s just a start. In this chapter, we will go further, building RESTful services that use hypermedia to model state transitions and describe business protocols.

When browsing the Web, we’re used to navigating between pages by clicking links or completing and submitting forms. Although we may not realize it, these interlinked pages describe a protocol—a series of steps we take to achieve a goal, whether that’s buying books, searching for information, creating a blog post, or even ordering a coffee. This is the very essence of hypermedia: by transiting links between resources, we change the state of an application.

Hypermedia is an everyday part of our online activities, but despite this familiarity, it’s rarely used in computer-to-computer interactions. Although Fielding’s thesis on REST highlighted its role in networked systems, hypermedia has yet to figure significantly in contemporary enterprise solutions.

The phrase hypermedia as the engine of application state, sometimes abbreviated to HATEOAS, was coined to describe a core tenet of the REST architectural style. In this book, we tend to refer to the hypermedia tenet or just hypermedia. Put simply, the tenet says that hypermedia systems transform application state.

A hypermedia system is characterized by the transfer of links in the resource representations exchanged by the participants in an application protocol. Such links advertise other resources participating in the application protocol. The links are often enhanced with semantic markup to give domain meanings to the resources they identify.

For example, in a consumer-service interaction, the consumer submits an initial request to the entry point of the service. The service handles the request and responds with a resource representation populated with links. The consumer chooses one of these links to transition to the next step in the interaction. Over the course of several such interactions, the consumer progresses toward its goal. In other words, the distributed application’s state changes. Transformation of application state is the result of the systemic behavior of the whole: the service, the consumer, the exchange of hypermedia-enabled resource representations, and the advertisement and selection of links.

On each interaction, the service and consumer exchange representations of resource state, not application state. A transferred representation includes links that reflect the state of the application. These links advertise legitimate application state transitions. But the application state isn’t recorded explicitly in the representation received by the consumer; it’s inferred by the consumer based on the state of all the resources—potentially distributed across many services—with which the consumer is currently interacting.

The current state of a resource is a combination of:

This last point emphasizes the fact that the state of a resource is partly dependent on the state of other local resources. The state of a sales order, for example, is partly a function of the state of a local copy of an associated purchase order; changes to the purchase order will affect the state of the sales order the next time the business rules governing the state of the sales order are evaluated (i.e., the next time a representation of the sales order is generated).

Importantly, the rules that control the state of a resource are internal to the service that governs the resource: they’re not made available to consumers. In other words, resource state is a function of a private ruleset that only the resource owner knows about: those rules don’t leak into the external representation.

Business rules that relate a resource to other resources should refer only to locally owned resources, however. This allows us to identify and prevent circular dependencies, whereby the state of resource A is partly a function of the state of resource B, which in turn is partly a function of the state of resource A, and so on. We can always arrange locally owned resources so as to prevent circular dependencies; we can’t do the same if the associated resources are governed by another service. If you need to relate the state of a resource to a third-party resource, we recommend making a local copy of the third-party resource using the Atom-based state alignment mechanisms described in Chapter 7.

A service enforces a protocol—a domain application protocol, or DAP—by advertising legitimate interactions with relevant resources. When a consumer follows links embedded in resource representations and subsequently interacts with the linked resources, the application’s overall state changes, as illustrated in Figure 5-1.

Consumers in a hypermedia system cause state transitions by visiting and manipulating resource state. Interestingly, the application state changes that result from a consumer driving a hypermedia system resemble the execution of a business process. This suggests that our services can advertise workflows using hypermedia. Hypermedia makes it easy to implement business protocols in ways that reduce coupling between services and consumers. Rather than understand a specific URI structure, a consumer need only understand the semantic or business context in which a link appears. This reduces an application’s dependency on static metadata such as URI templates or Web Application Description Language (WADL). As a consequence, services gain a great deal of freedom to evolve without having to worry whether existing consumers will break.

When developing a service we abstract away implementation details from consumers, thereby decreasing coupling. But no matter the degree of loose coupling, consumers must have enough information available in order to interact with our service. We need to provide some way for them to bind to our service and drive the supported application protocol. The beauty of hypermedia is that it allows us to convey protocol information in a declarative and just-in-time fashion as part of an application’s resource representations.[43]

For computer-to-computer interactions, we advertise protocol information by embedding links in representations, much as we do with the human Web. To describe a link’s purpose, we annotate it. Annotations indicate what the linked resource means to the current resource: “status of your coffee order,” “payment,” and so on. For annotations we can use microformats or Semantic Web technologies, or we can design our own application-specific formats. We call such annotated links hypermedia controls, reflecting their enhanced capabilities over raw URIs.

To illustrate the key aspects of hypermedia-driven services, we’ll build the ordering and payment parts of Restbucks’ service using a custom hypermedia format.

Hypermedia-driven distributed systems put similar demands on their consumers as the Web does on humans: consumers need to discover and interact with resources so that they can realize an application’s goal. To illustrate how representation formats allow consumers to discover and interact with resources, let’s consider XHTML, one of the most popular representation formats on the World Wide Web. XHTML is used to represent information on a page (its business payload) and to link to other pages or content (its protocol description). The inclusion of links to other resources makes XHTML a hypermedia format. As humans, we take this property for granted. We use web browsers to move from one page to another without thinking of the underlying mechanics (which is a good thing, of course). Browsers apply the hypermedia tenet and interpret links to show possible transitions from one resource (page) to another.

The Web is agnostic to the representation formats exchanged by consumers and services, which is one of the primary reasons for its success in diverse domains. But when it comes to hypermedia, not all formats are equal.

Despite the success of hypermedia formats on the Web, today’s distributed applications typically use nonhypermedia formats such as plain XML to integrate systems. Although XML is easy to use as a data interchange format, and despite its near ubiquity, it is utterly oblivious to the Web. This is neatly demonstrated by our humble order XML representation from Chapter 4, which we show again in Example 5-1.

There’s nothing intrinsically wrong with this order representation when considered in isolation. After all, it conveys the current state of the order well enough. But it fails to provide any context; that is, it doesn’t indicate the current state of the business process, or how to advance it. Informally, we know we need to pay for a drink once ordered, but the representation of Example 5-1 doesn’t indicate how to make that payment.

The use of plain XML leaves the consumer without a guide—a protocol—for successfully completing the business transaction it has initiated. Because there are no hypermedia controls in the order representation, the consumer must rely on out-of-band information to determine what to do next. From a loose coupling point of view, that’s a poor design decision. Aspects of the service’s implementation leak through mechanisms such as URI templates into the consumer’s implementation, making change difficult and risky.

We can, of course, communicate protocol information to the developers of a consumer application using written documentation, or static contracts such as Web Services Description Language (WSDL), WADL, or URI templates. But, as we’ll see, the Web and hypermedia enable us to do better.

Let’s consider first how Restbucks might communicate protocol information if it chose the static, upfront approach. As we wrote in Chapter 3, Restbucks could share URI templates with its consumers. For example, it could document and share the template http://restbucks.com/payment/{order_id}. The documentation would describe how consumers are expected to PUT a payment representation to a URI generated by replacing the order_id part of the template with the ID of the original order. It’s hardly rocket science, and with a little URI manipulation it can quickly be made to work. Unfortunately, it can be made to fail just as rapidly.

If Restbucks chose to publish URI templates to consumers, it would then be bound to honor those templates for the long term, or risk breaking existing consumer applications. Publishing URI template details outside a service’s boundary exposes too much information about the service’s implementation. If the implementation of the ordering and payment services were to change, perhaps as a result of outsourcing the payment capability to a third party, there’d be an increased risk that consumers built to the (now defunct) http://restbucks.com/payment/{order_id} template would break. Since that kind of business change happens frequently, Restbucks ought to encapsulate its implementation details as far as possible.

Generally, it’s better to expose only stable URIs. These stable URIs act as entry points to services, after which hypermedia takes over. For example, the entry point to the Restbucks ordering service is http://restbucks.com/order. Interacting with the resource at that URI generates further resource representations, each of which contains hypermedia links to yet more resources involved in the ordering business process.

This doesn’t mean URI templates are a bad idea. In fact, they are an excellent metadata format. But as with all good things, we must learn how to use them in moderation. We believe URI templates are an excellent means for documenting service design. Restbucks’ implementations all embrace URI templates for internal documentation and implementation purposes. However, those same implementations only ever share completed (opaque) URIs with consumers. The templates remain a private design and development detail.

Formats provide the means for interacting with a service, and as such they’re part of that service’s contract. Because a format is part of the service contract, it’s important to choose an appropriate hypermedia format at design time.

REST’s hypermedia tenet doesn’t prescribe a specific representation format, but it does require a format capable of conveying necessary hypermedia information. Different hypermedia formats suit different services. The choice depends on a trade-off between reach and utility—between the ability to leverage existing widely deployed software agents and the degree to which a format matches our domain’s needs.

Several of the hypermedia formats already in use on the Web today are capable of supporting some of our requirements. Formats such as Atom[44] (and RSS) and XHTML are widely used and understood. Correspondingly, many software tools and libraries are available to produce, consume, and manage resource representations in these formats. Web browsers, for example, know how to render XHTML representations as pages, and Atom representations as lists of entries.

However, widespread tool support alone doesn’t make a format suitable for every domain. For example, XHTML supports hypermedia (and is therefore capable of describing business protocols) because it implements hypermedia controls such as <a href>. Still, as Example 5-2 shows, it’s a verbose format for representing a Restbucks order in a computer-to-computer interaction.

By encoding our order as XHTML, we are able to render it in a web browser, which is helpful for debugging. We leverage XHTML controls, such as the class attribute, to convey semantics about the contents of an element. The approach of mixing business data with web page presentation primitives has been popularized through microformats, which we’ll discuss in Chapter 10. Example 5-2 illustrates, however, how the fusion of business data and presentation primitives comes at the expense of some noisy XHTML markup. In choosing XHTML, we make the conscious decision to trade expressiveness for reach by shoehorning a specific application format into a more general format.

But sometimes, trading expressiveness for reach isn’t the right thing to do. Do our consumers really need to understand XHTML and all its verbiage? Given that Restbucks isn’t concerned with user-facing software (browsers), XHTML appears more an overhead than a benefit. In this case, it’s probably better to devise our own hypermedia format.

Because the Web is agnostic to representation formats, we’re free to create custom formats tailored to our own problem domains. Whether we use a widely understood format or create our own, hypermedia formats are more web-friendly than nonhypermedia formats. A good hypermedia format conveys both domain-specific and protocol information. Domain-specific information includes the values of information elements belonging to a business resource, such as the drinks in an order. Protocol information declares how to make forward progress in a business process—how to move from ordering to payment, for example.

In a hypermedia format, hypermedia controls represent protocol information. A hypermedia control includes the address of a linked resource, together with some semantic markup. In the context of the current resource representation, the semantic markup indicates the meaning of the linked resource.

Creating a domain-specific hypermedia format isn’t as difficult as it might seem. In Restbucks’ case, we can build on the XML schemas we’ve already created. All we have to do is to introduce additional elements into the representations. We can add hypermedia controls to these schemas by defining both a link and the necessary semantic markup. Example 5-3 shows a first attempt at adding an application-specific hypermedia control to an order.

In this format, the order representation contains a proprietary <payment> element. <payment> is a hypermedia control.

If we wanted to represent hypermedia controls in this manner, we would specify in our format description that a <payment> element indicates “a linked resource responsible for payments relating to the current order.” A consumer wanting to make a payment now knows which resource it needs to interact with next.

But that’s not the only way to add hypermedia controls to our representation format. In fact, we believe it’s a suboptimal method because it results in multiple elements with almost identical link semantics, but different protocol semantics. <payment> bears the joint responsibility of being both a link and a semantic annotation. If we added a <cancel> element to our scheme, this new element would have exactly the same link semantic as <payment>, but a wholly different protocol semantic.

Our preferred approach is to separate concerns by distinguishing between the act of linking and the act of adding meaning to links. Linking is a repeatable process. The meanings we attach to links, however, change from context to context. To achieve this separation of concerns, we define a <link> element to convey the domain-agnostic link function, and a rel attribute to represent the application semantics associated with a particular link. Composing this <link> element into Restbucks’ representation format is easy, as shown in Example 5-4.

The Restbucks hypermedia format specification would have to document the meaning of the rel attribute’s payment value so that consumers understand the role of the linked resource in relation to the current resource.

By adding a <link> element to our order schema, we’ve successfully defined our own custom hypermedia format. Designing our own format allows us to express our specific application needs, yet retain the benefits of hypermedia. Of course, as with any design trade-off, there are downsides. For example, the representations in Examples Example 5-3 and Example 5-4 don’t have the same reach—the ability to be processed by widely deployed generic clients—as more established hypermedia formats such as XHTML. In essence, we’re creating a closed hypermedia ecosystem—one that’s specific to Restbucks. This ecosystem is for consumers who are prepared to process our domain-specific payloads. Though this limits an application’s reach, for Restbucks and enterprise systems in general, it might be just what we need.

Introducing a custom hypermedia format feels like a step in the right direction. Now consumers of Restbucks services can receive both business data and the information necessary to drive the ordering and payment protocol, all in a concise and expressive format specific to the business domain.

Still, we need to convey to consumers how to process and reason about these representations. Fortunately, HTTP provides a way of identifying particular media type representations using the Content-Type header (e.g., Content-Type: application/xml, which we have used in previous chapters).

Grafting hypermedia controls onto XML documents is easy, but it’s only half the story. What we really need to do is to create a media type. A media type specifies an interpretative scheme for resource representations. This scheme is described in terms of encodings, schemas, and processing models, and is a key step in creating a contract for a service.

A media type name (such as application/xml) is a key into a media type’s interpretative scheme. Media types are declared in Content-Type HTTP headers. Because they’re situated outside the payload body to which they’re related, consumers can use them to determine how to process a representation without first having to crack open the payload and deeply inspect its content—something that might require relatively heavy processing, such as a decrypt operation, for example.

A media type value indicates the service’s preferred scheme for interpreting a representation: consumers are free to ignore this recommendation and process a representation as they see fit.

For example, we know that XHTML is valid XML and can be consumed by any software that understands XML. However, XHTML carries richer semantics (it supports hypermedia) than plain XML, and so processing it as XML rather than XHTML is lossy—we lose information about linked resources.

If a consumer interprets a received representation ignoring the rules set out by the media type in the accompanying Content-Type header, all bets are off.

Media types are one of three key components of DAPs. The other two components are link relation values, which describe the roles of linked resources, and HTTP idioms, which manipulate resources participating in the protocol. Link relation values help consumers understand why they might want to activate a hypermedia control. They do so by indicating the role of the linked resource in the context of the current representation.

A media type value helps a consumer understand what is at the end of a link. The how of interacting with a resource is realized by HTTP constructs such as GET, PUT, and POST (and their conditional counterparts) and the control alternatives suggested by the HTTP status codes.

As we described earlier, we chose to stick with XML for the Restbucks representation formats. This decision allows us to reuse existing schemas/formats in our media type description.[46] However, this doesn’t mean we should use text/xml or application/xml as the value of the Content-Type header, and for good reason. The Content-Type header sets the context for how the payloads should be processed. Suggesting that the payload is just XML gives the wrong indication to software agents about the content and processing model for a representation. Treating Restbucks content and its hypermedia control format as plain XML simply leads to a hypermedia dead end.

For example, in Example 5-5 we see a Restbucks order, which contains two <link> elements advertising other resources of interest to the customer. Using this simple protocol representation format, the service shows consumers how they can make forward progress through the business process by interacting with the payment and special offer resources.

But all is not well with Example 5-5. While the root XML namespace of the payload clearly indicates that this is a Restbucks order (and is therefore hypermedia-friendly, as defined by the Restbucks specification), the Content-Type header declares it should be processed as plain XML, not as the hypermedia-friendly application/vnd.restbucks+xml. When we encounter an HTTP payload on the Web whose Content-Type header is set to application/xml, we’re meant to process that payload in accordance with its media type specification, as set out in RFC 3023.

By treating XML hypermedia formats as plain XML, we skip many of their benefits. The interpretative scheme for each format includes hypermedia control definitions that enable programs to identify and process hypermedia controls embedded within a document of that type. These processing imperatives do not exist in the application/xml media type specification, which means that the payload of Example 5-5 should be treated simply as structured data. The protocol information (the <link> elements) will appear as odd-looking business information.

XML thinking encourages us to separate protocol and data—usually to our detriment. Too often, we end up designing containers for data, with no inline provision for protocol information. This leads us to advertise the protocol using an out-of-band mechanism such as URI templates. The burden then falls to consumer applications to keep current with changes in the service implementation (particularly around URIs)—changes that ordinarily the service would not be obliged to share.

Adding hypermedia controls to an XML representation doesn’t help much if we then go on to recommend the representation be treated as plain XML. The controls can play their part in a hypermedia system only if the Content-Type header suggests using a hypermedia-aware interpretative scheme. This is the case even if the document’s root XML namespace alludes to a useful processing model. Content-Type headers, not XML namespaces, declare how a representation is to be processed: that’s the convention on the Web.

A diligent consumer might later examine the XML namespace or associated schema and discover a more specialized type. Activities such as this take place outside the well-understood, predictable mechanisms defined by HTTP. Wrongly inferring the processing model may even harm the application if the originating service explicitly meant for the payload to be interpreted in a specific way—not all representations with angle brackets are well-formed XML, after all.

For example, as part of its monitoring processes, Restbucks may produce a feed of malformed orders: corrupted documents submitted by inept or malicious customers. In the Content-Type header, the service indicates that the representation should be treated as text/plain. Consumers that decide to treat these representations as XML, because they contain angle brackets, had better guard against exceptions arising from malformed XML since the service has made no stronger commitment than to provide plain text.

Balancing the number of media types we use against the number of representation formats that our DAP uses can be a tricky affair. On the one hand, it’s possible to create a new media type for each representation format hosted by a service. On the other hand, we might choose to create one media type for the entire application domain.

Creating a one-to-one mapping between media types and representation formats, with specialized media type values such as application/vnd.restbucks.order+xml and application/vnd.restbucks.payment.creditCard+xml, can lead to extremely tight coupling between a service’s domain layer and its consumers. The interactions in the application protocol might have to be fine-grained since the composition of representation formats will not be possible, given that there can be only one media type per HTTP request or response. At the other extreme, a single monolithic media type can add unnecessary overhead when we want to share a subset of schemas between application domain contexts.

In our case, we’ve chosen to make application/vnd.restbucks+xml generally applicable to the entire domain of orders and payments in Restbucks. As a result, our media type defines the order, payment, and receipt schemas, and our chosen hypermedia control format and processing model (the <link> element). It also defines a number of link relation values, which our DAP uses to identify the relationship between resources.

In the Restbucks application domain, we assume that consumers who understand the application/vnd.restbucks+xml media type are capable of dealing with everything defined by it. However, it occasionally happens that some consumers want to handle only a subset of the representation formats defined in a media type. While there is no standard solution to this issue on the Web, there is a popular convention defined by the Atom community. The application/atom+xml media type defines both the feed and the entry resource representation formats.[47] While the vast majority of consumers can handle both, there is a small subset wishing only to deal with standalone entries. In recognition of this need, Atom Publishing Protocol (AtomPub) added a type parameter to the media type value (resulting in Content-Type headers such as application/atom+xml;type=entry). With such a value for the ContentType header, it is now possible to include “entry” resource representations as payloads in HTTP requests or responses without requiring that the processing software agents have a complete understanding of all the Atom-defined formats.

In many of the examples in the remainder of this chapter, we’ll omit the HTTP headers and focus on the hypermedia payloads. These examples assume the Content-Type header is set to application/vnd.restbucks+xml.

Contracts are a critical part of any distributed system since they prescribe how disparate parts of an application should interact. Contracts typically encompass data encodings, interface definitions, policy assertions, and coordination protocols. Data encoding requirements and interface definitions establish agreed-upon mechanisms for composing and interpreting message contents to elicit specific behaviors. Policies describe interoperability preferences, capabilities, and requirements—often around security and other quality-of-service attributes. Coordination protocols describe how message exchanges can be composed into meaningful conversations between the disparate parts of an application in order to achieve a specific application goal.[48]

The Web breaks away from the traditional way of thinking about upfront agreement on all aspects of interaction for a distributed application. Instead, the Web is a platform of well-defined building blocks from which distributed applications can be composed. Hypermedia can act as instant and strong composition glue.

Contracts for the Web are quite unlike static contracts for other distributed systems. As Figure 5-2 shows, contracts are a composition of a number of aspects, with media types at their core. Protocols extend the capabilities of a media type into a specific domain. Currently, there is no declarative notation to capture all aspects of a contract on the Web. While technologies such as XML Schema allow us to describe the structure of documents, there is no vocabulary that can describe everything. As developers, we have to read protocol and media type specifications in order to implement applications based on contracts.

Underpinning all media types and protocols is the HTTP uniform interface, which provides the plumbing through which contracts are enacted at runtime. Even with media types and protocols to describe a contract, consumers still need to know how individual resources should be manipulated at runtime. In other words, contracts define which HTTP idioms—methods, headers, and status codes—consumers should use to interact with a linked resource in a specific context.

Such information can come from several sources. Many hypermedia controls have attributes that describe transfer options. XHTML’s <form> element, for example, includes a method attribute that specifies the HTTP method to use to send form data. Occasionally, the current application context can be used to determine which idiom to use next. If the consumer receives a representation accompanied by an ETag header, it’s reasonable to assume that subsequent requests for the same resource can be made using a precondition: If-Match or If-None-Match, as appropriate. Similarly, a 303 See Other status code and accompanying Location header instruct the recipient to perform a GET on the Location header’s URI. When neither the current payload nor the processing context indicates which idioms to use, OPTIONS can be used on the linked resource’s URI.

REST introduces a set of tenets that, when applied to distributed systems design, yield the desirable characteristics of scalability, uniformity, performance, and encapsulation. Using HTTP, URIs, and hypermedia, we can build systems that exhibit exactly the same characteristics. These three building blocks also allow us to implement application protocols tailored to the business needs of our solutions.

As a web-based system, Restbucks supports a DAP for ordering and payment. Figure 5-4 summarizes the HTTP requests that the ordering service supports and the associated workflow logic each request will trigger.

The permitted interactions shown in Figure 5-4 constitute a complete business protocol for lodging, managing, and paying for an order. Each interaction invokes a workflow activity that changes the underlying state of one or more resources managed by the service.[51] Modeling Restbucks’ business processes as a DAP and then representing that protocol as a state machine in this manner is a useful way of capturing a business process at design time.

Moving from design to implementation, we need to think about the protocol in a slightly different way. In a resource-oriented distributed application, an application protocol can be thought of as a function of one or more resource life cycles and the business rules that connect these resources. Because of its resource-centric nature, the Restbucks service does not host an application protocol state machine. In other words, there’s no workflow or business logic for the application protocol as such. Rather, the service governs the life cycles of the orders and payments participating in the application protocol. Any workflows in the service implementation relate to resource life cycles, not the application protocol life cycle. While we’ve been explicit in modeling the business process as an application protocol state machine, we’ve been diligent in implementing it wholly in terms of resource state machines.

Figure 5-5 shows the resource state machine for an order as implemented in the service. From this diagram and Figure 5-4, we can derive the DAP:

  • POST creates an order.

  • Any number of POSTs updates the order.

  • A single DELETE cancels the order, or a single PUT to a payment resource pays for the order.

  • And finally, a single DELETE confirms that the order has been received.

The state machine diagram in Figure 5-5 is a useful design artifact, but it isn’t a good representation format for sharing protocols over the Web. For that, we use hypermedia, which starts with a single, well-known entry point URI.

For our hypermedia service implementation, we’ll create an entry point to the ordering service at http://restbucks.com/order. To initiate the ordering protocol, a consumer POSTs a request with an order representation to http://restbucks.com/order, which results in a new order being created. The payload of the POST request must be a Restbucks order XML representation, and the Content-Type header must contain the value application/vnd.restbucks+xml.

Changing the media type to application/vnd.restbucks+xml from application/xml might seem a modest step, but in doing so we’ve realized some fundamental goals: the entry point to the Restbucks service gives consumers a way of triggering an instance of our DAP. From this point onward, hypermedia takes over, with link relations from the Restbucks media type leading consumers from one step in the business protocol to the next. If all goes well, by the end of the process we will have the set of interactions shown in Figure 5-6.

Figure 5-6 shows the trace of HTTP interactions that occur during a normal, successful interaction with Restbucks. Each interaction is guided by hypermedia links, so it’s easy to piece this puzzle together, starting from the beginning.

If Restbucks accepts the POSTed order, the ordering service generates a 201 Created response, which contains a representation of the service’s version of the order. So far, this matches the CRUD approach we saw in the preceding chapter. The marked differences are in the use of the Content-Type header, and in the contents of the returned resource representation, which now uses links to advertise the next set of accessible resources.

At this point, the ordering protocol allows the consumer to check the status of the order, cancel it, update it, or pay for it (see Figure 5-7). For example, if the consumer checks the status of the order prior to paying, the service returns a representation with a business payload part depicting the status of the order, and a protocol part indicating payment is still required, as shown shortly in Example 5-6.

The semantics of the links in the order representation must be communicated to, and understood by, Restbucks’ customers. This is the purpose of the Restbucks media type.[53] By accepting HTTP responses with the Content-Type header set to application/vnd.restbucks+xml, consumers enter into a contract that requires them to understand Restbucks’ representation format and link relation values if they wish to make forward progress. As per the media type description, Restbucks leverages a reusable <link> element to convey business protocol information. We separate the <link> element and its attributes from the rest of the Restbucks representation format elements and we define it in its own http://schemas.restbucks.com/dap namespace:

  • <link> elements have a uri attribute whose value indicates a resource with which the consumer can interact to progress the application protocol.

  • <link> elements have a rel attribute containing semantic markup. The definitions of the markup values imply which HTTP verb to use when following the link, as well as required HTTP headers, and the structure of the payload.

  • If a request requires an entity body, the <link> element will contain a mediaType attribute that declares the format of the request payload. If a request does not require an entity body, the mediaType attribute will be absent.

With the <dap:link> hypermedia control at our disposal, let’s see an example order representation. Example 5-6 shows a response representation generated immediately after an order has been accepted.

This order representation shows the different URIs and associated rel values consumers can use to advance the DAP. The semantic markup definitions are shared widely with consumers as part of the media type specification, and are as follows:

At this point in the workflow, if the customer GETs the status of the order via http://restbucks.com/order/1234, the customer will be presented with the resource representation shown in Example 5-6. Once payment has been PUT to the payment URI, however, subsequent requests for the order will return a representation with different links, reflecting a change in application state and the opening up of a new set of steps in the DAP.

At any point in the execution of the ordering process, the service can inject hypermedia controls into response bodies. For example, if the customer submits a payment via POST, as shown in Example 5-7, the service updates the state of the order to reflect the fact that payment is no longer required.

The service then injects links to both the order and receipt resources into the response, as shown in Example 5-8.

In the newly minted representation in Example 5-8, we have two <link> elements to consider. The order link takes the customer directly to the order resource (where the customer can complete the ordering protocol) while the receipt link leads to a receipt resource (should the customer need a receipt).

If the customer requires a receipt, a GET on the receipt link returns the representation in Example 5-9. If the customer doesn’t want a receipt, it can follow the order link in Example 5-8 directly back to the order. Whichever route is chosen, the customer ends up at a point in the workflow where the order is paid for and we have a representation similar to the one in Example 5-10.

Example 5-9 contains two elements that allow us to infer the state of the distributed application. The <paid> element, which contains a timestamp, provides business-level confirmation that the order has been paid for. This sense of application state is reinforced at the protocol level by the presence of a single <link> element, which directs the consumer toward the order resource and the end of the business process. And because the payment link is now absent, the consumer has no way of activating that part of the protocol.

We know that having paid for the order, the customer might GET a receipt—and following that, the latest order representation—or the customer might simply go straight to the latest representation of the order. Either way, the customer eventually ends up with the order information shown in Example 5-10.

There are two things to note here. First, the value of the <status> element has changed to preparing. Second, there is only one possible transition the customer can initiate, which is to request the status of the order through the self link.

While the order is in this state, every inquiry regarding the status of the order will receive the same response. This is the equivalent of the service saying, “Your order is being prepared; thank you for waiting.” Every time the customer GETs the order representation, it will see the same preparing value, until the barista delivers the coffee.

Once the order is complete, the barista changes the status of the underlying resource to ready. This is an example of how an internal, backend business process can change the state of a resource without consumer intervention. The next time the customer GETs the order resource representation, the response will include the final possible transition in the ordering process, as shown in Example 5-11.

The representation in Example 5-11 includes a single, final state transition in the ordering process:

receipt

Customers can DELETE the linked resource, thereby completing the order.

This DELETE request takes the receipt from Restbucks, at least as far as the consumer is concerned. It’s the logical equivalent of physically taking a receipt from the cashier’s hand, and in doing so completing the order process.

Inside the service, we probably wouldn’t remove the resource, but instead maintain it as part of Restbucks’ audit trail. This final resource state transition has the effect of completing the order, and transitions our DAP to its final state.

The response to DELETE transfers a final copy of the order. The representation, as shown in Example 5-12, has no hypermedia constructs, indicating that the resource is not part of an active business process.

And with that, our protocol instance is complete. But our exploration into hypermedia isn’t—at least not yet.

One advantage of using hypermedia to advertise protocols is that we can introduce new features without necessarily breaking existing consumers. The media type application/vnd.restbucks+xml contains numerous schemas and link relation values, not all of which are required for the basic ordering workflow; some of them are for optional interactions, such as special offers, which Restbucks occasionally runs.

For example, Restbucks might run a loyalty program based on coffee cards: after a customer places nine coffee orders, the tenth drink is free. To allow consumers to create or update a coffee card, Restbucks adds a link to the receipt representation returned after payment has been taken and the drinks dispensed, as shown in Example 5-13.

Customers that don’t understand the semantics of the coffee-card link are free to ignore it—they just won’t get any free drinks. Customers who do understand the semantics of the http://relations.restbucks.com/coffee-card relation but who don’t already have a coffee card can issue a simple GET request to the URI identified by the coffee-card link. The response contains the representation of a new coffee card with the coffee that was just purchased already recorded, as shown in Example 5-14.

The coffee card’s resource representation in Example 5-14 contains a self link, which identifies the card; a <numberOfCoffees> element, which records how many coffees have been purchased using the card; and a tamper-proofing mechanism, which allows Restbucks to determine whether malicious customers have adjusted the card’s data.[54]

If a customer has a coffee card from a previous purchase, the customer can update it by POSTing it to the identified URI. Doing so updates both the number of coffees purchased and the tamper proofing. In accordance with the business rules around the promotion, if the presented card already carries enough endorsements to obtain a free coffee, a new card will be generated; this new card will then be returned in the response.

Note

There is no correlation between the coffee card and a specific order, despite the format of the URI in Example 5-13. Remember, URIs are opaque to consumers. In this case, the link contains information that the Restbucks ordering service uses when updating the count of endorsements in the coffee card.

Upon successfully accepting and updating the customer’s coffee card, the service returns the latest representation of the coffee card resource using a 200 OK response, as per Example 5-15.

Spending fully endorsed coffee cards is simple: at the payment step, customers present their card toward full or partial fulfillment of the bill. While Restbucks is running the promotion, the order’s set of hypermedia controls is extended to encompass this activity, as shown in Example 5-16.

Customers that don’t want (or are unable) to participate in a promotion simply ignore the coffee-card-payment hypermedia control. Customers that do want to participate simply POST their endorsed card to the coffee-card-payment URI (see Example 5-17).

If the coffee card payment covers the bill, the customer receives a payment confirmation as per Example 5-8. If not, the customer receives another order representation with the <cost> element adjusted to reflect the value of the submitted coffee card.

Our discussion to this point has concentrated on using hypermedia to model and implement business protocols. But hypermedia has other uses, including the provision of network-friendly data models.

On the Web, pages and other media are composed together using links. A web browser fetches a web page and then fetches other resources, such as images and JavaScript. The browser renders the page and exposes links to the user to support page transitions.

This model respects the underlying network. Information is loaded as lazily as possible (but no lazier), and the user is encouraged to browse pages—traverse a hypermedia structure—to access information. Breaking information into hypermedia-linked structures reduces the load on a service by reducing the amount of data that has to be served. Instead of downloading the entire information model, the application transfers only the parts pertinent to the user.

Not only does this laziness reduce the load on web servers, but the partitioning of data across pages on the Web allows the network infrastructure itself to cache information. An individual page, once accessed, may be cached for up to a year (the maximum allowed by HTTP) depending on how the service developer configures the service. As a result, subsequent requests for the same page along the same network path will be satisfied using a cached representation, which in turn further reduces load on the origin server.

Importantly, the same is true of computer-to-computer systems: hypermedia allows sharing of information in a lazy and cacheable manner. For example, if Restbucks wanted to share its complete menu with other systems, it could use hypermedia to split the menu details across separate resources. This would allow different resource representations to be cached for different lengths of time, depending on the business use. Coffee descriptions, for example, might be long-lived, while pricing might change daily. Examples Example 5-18 and Example 5-19 show some of these hypermedia-linked representations.

As Examples Example 5-18 and Example 5-19 show, a large information model such as the Restbucks menu can easily be partitioned for network access using hyperlinks.

It’s quite valid to mix structural and protocol hypermedia in a representation. But there are other options: some systems may choose to split hypermedia controls from business payload in their representations. In Restbucks, for example, we could choose to separate the representation of an order from its DAP links. We’d then put the DAP links into a separate resource, as shown in Example 5-20. rel=“http://relations.restbucks.com/dap” indicates that the consumer can dereference the link to establish the next legal steps in the DAP.

The decision on what should be decomposed into separate, or even overlapping, resources is part of the design process for a service. In making these decisions, we need to consider numerous design factors:

Implementing a hypermedia service might seem at first to be an intimidating prospect, but in practice, the overhead of building a hypermedia system is modest compared to the effort of building a CRUD system. Moreover, the effort generally has a positive return on investment in the longer term as a service grows and evolves. Although the implementation details will differ from project to project, there are three activities that every service delivery team will undertake throughout the lifetime of a service: designing protocols, choosing formats, and writing software.

We’ve been describing Restbucks’ DAP and formats throughout this chapter, so we’re already one step toward a working implementation.

To build the ordering service in Java, we need only two framework components: a web server and an HTTP library. On the client side, we need only an HTTP library. For these tasks, we’ve chosen Jersey[55] (a JAX-RS[56] implementation), which provides the HTTP plumbing for both the service and its consumers, and the Grizzly web server, because it works well with Jersey. Apart from framework support, all we need is a handful of patterns for services and consumers, beginning with the server-side architecture.

The Java server-side architecture is split across several layers, as shown in Figure 5-8.

Although crucial to the deployment of a working service, the web server implementation is less important architecturally. Fortunately, it is abstracted from the service developer through the JAX-RS layer. The JAX-RS layer—although its name implies much more—simply provides a friendly programmatic binding to the underlying web server.

Using JAX-RS, we declare a set of methods, to which the framework routes HTTP interactions. Inside the service, resources act as controllers for workflow activities, passing through business information extracted from the representations and marshalling results and exceptions into HTTP responses.

Workflow activities implement the individual stages of the Restbucks workflow in terms of resource life cycles: creating orders, updating orders, canceling orders, creating payments, and delivering completed orders to customers. They’re also responsible for changing the state of the underlying domain objects, which in turn are persisted in repositories.

Though their value in partitioning work into smaller, more manageable units is obvious, workflow activities provide more than just a unit of work abstraction; they also provide choreography between tasks.

Each activity knows which downstream activities are valid. For example, if payment succeeds, the valid next steps are to ask for a receipt or to check the order status. No other activities are valid, and any attempt to do anything else will result in an error being propagated to the consumer via an HTTP status code. Knowing which activities are valid given the current state of current resources, the service can advertise the next steps in the protocol by embedding hypermedia controls in the representations sent to the consumer.

The hypermedia controls that the service makes available to the consumer describe the parts of the DAP the consumer can use to drive the service through the next stages of its business workflow, as we see in Figure 5-9.

Each resource in Figure 5-9 is internally identified by its URI or URI template. The well-known entry point URI /order is the only URI consumers are expected to know before interacting with the service. The URIs of all other resources (whose templates are used for internal documentation only) are discovered at runtime.

In accordance with Restbucks’ DAP, when the service receives a valid order at the entry point URI, it returns a confirmation. This response contains an order representation augmented with additional resource state, including cost and status. Importantly, it also contains links the consumer can use to progress the workflow. In Figure 5-9, the hypermedia-enhanced order representation returned to the consumer contains links to both the order resource and a payment resource.

The pattern repeats for the payment resource. When the service receives a valid payment representation, it generates an enhanced representation containing links to other resources with which the consumer can interact: the order resource (to check status) and a receipt resource (to request a receipt).

Underlying the DAP is code, of course. In the Java implementation, resource behavior is implemented by one or more activity classes, as shown in Figure 5-10.

Each activity in Figure 5-10 is bound to a particular URI and verb. For example, the UpdateOrderActivity is triggered by the consumer POSTing a valid order representation to URIs matching /order/{orderId}. Similarly, PaymentActivity is triggered by a PUT with a valid payment representation to /payment/{paymentId} and the protocol is completed with a DELETE request to /receipt/{orderId}.

Here, we’re concerned simply that valid representations are transferred via the correct HTTP verb to a resource in the appropriate state. Consumers are corralled toward making the right request at the right time by the semantically annotated hypermedia controls in the representations they receive.

Jersey helps provide a programmatic abstraction over a web server, but it doesn’t help create hypermedia and DAPs. Because of this, most of the code in the service implementation is our own. In writing this code, we’ve devised some patterns to separate out the concerns of protocols from the concerns of the business activities they coordinate. With that in mind, let’s walk through the implementation.

The resource classes OrderResource, PaymentResource, and ReceiptResource expose the service implementation to the Web through Jersey. In our implementation, all JAX-RS code is localized to the resource classes. Framework code is not allowed to penetrate deeper into the service implementation—we prefer most of our code to be (easily testable) Plain Old Java Objects (POJOs).

The OrderResource class shown in Example 5-21 is typical of the resources in the ordering service implementation.

Example 5-21. OrderResource class

@Path("/order")
public class OrderResource {

  private @Context UriInfo uriInfo;

  @GET
  @Path("/{orderId}")
  @Produces("application/vnd.restbucks+xml")
  public Response getOrder() {
    try {
      OrderRepresentation responseRepresentation = new ReadOrderActivity()
                     .retrieveByUri(new RestbucksUri(uriInfo.getRequestUri()));
      return Response.ok().entity(responseRepresentation).build();
    } catch(NoSuchOrderException nsoe) {
      return Response.status(Status.NOT_FOUND).build();
    } catch (Exception ex) {
      return Response.serverError().build();
    }
  }

  @POST
  @Consumes("application/vnd.restbucks+xml")
  @Produces("application/vnd.restbucks+xml")
  public Response createOrder(String orderRepresentation) {
    try {
      OrderRepresentation responseRepresentation = new CreateOrderActivity()
      .create(OrderRepresentation.fromXmlString(orderRepresentation).getOrder(),
                                     new RestbucksUri(uriInfo.getRequestUri()));
     return Response.created(
            responseRepresentation.getUpdateLink().getUri())
              .entity(responseRepresentation).build();
   } catch (InvalidOrderException ioe) {
     return Response.status(Status.BAD_REQUEST).build();
   } catch (Exception ex) {
     return Response.serverError().build();
   }
  }

  @DELETE
  @Path("/{orderId}")
  @Produces("application/vnd.restbucks+xml")
  public Response removeOrder() {
    try {
      OrderRepresentation removedOrder = new RemoveOrderActivity()
                      .delete(new RestbucksUri(uriInfo.getRequestUri()));
      return Response.ok().entity(removedOrder).build();
    } catch (NoSuchOrderException nsoe) {
      return Response.status(Status.NOT_FOUND).build();
    } catch(OrderDeletionException ode) {
      return Response.status(405).header("Allow", "GET").build();
    } catch (Exception ex) {
      return Response.serverError().build();
    }
  }

  @POST
  @Path("/{orderId}")
  @Consumes("application/vnd.restbucks+xml")
  @Produces("application/vnd.restbucks+xml")
  public Response updateOrder(String orderRepresentation) {
    try {
      OrderRepresentation responseRepresentation = new UpdateOrderActivity()
                        .update(
                         OrderRepresentation.fromXmlString(
                          orderRepresentation)
                         .getOrder(), new RestbucksUri(uriInfo.getRequestUri()));
     return Response.ok().entity(responseRepresentation).build();
   } catch (InvalidOrderException ioe) {
     return Response.status(Status.BAD_REQUEST).build();
   } catch (NoSuchOrderException nsoe) {
     return Response.status(Status.NOT_FOUND).build();
   } catch(UpdateException ue) {
     return Response.status(Status.CONFLICT).build();
   } catch (Exception ex) {
     return Response.serverError().build();
   }
  }
}

The JAX-RS annotations bridge the Web and the service implementation (activities in our case). Methods are invoked in response to a combination of a specific verb and URI or URI template, and each method consumes and produces a representation with a particular media type.

To illustrate, in Example 5-21 the updateOrder method is invoked whenever a representation with media type application/vnd.restbucks+xml is POSTed to the path[57] /order/{orderId} concatenated with the service URI and web application context (e.g., http://restbucks.com/). The media type, verb, and path elements are associated with the method using the annotations @Consumes, @POST, and @Path, respectively. If the invocation is successful, the service produces a response with media type application/vnd.restbucks+xml, as declared by the @Produces annotation.

All the public methods in the resource classes follow a similar pattern. For operations that require a received representation, the JAX-RS framework provides one. Domain objects are instantiated from such received representations and passed into a workflow activity for processing. We also pass the request URI into the workflow activity so that the activity can generate response links with the same base path. Once the workflow activity has completed, the representation it generates is packaged as an HTTP response with the appropriate status code and sent back to the consumer.

If an exception occurs during the execution of a workflow activity, an HTTP response is generated to reflect the failure. For instance, if the consumer sends an invalid order representation to the /order or /order/{orderId} URI, an InvalidOrderException is raised in the UpdateOrderActivity. This exception is translated into an HTTP 400 Bad Request response by the OrderResource class. If there’s no specific catch block for an exception, the service responds with an HTTP 500 Internal Server Error.

The representation classes in the service are much like the underlying domain objects, except for two things: they’re marked up with JAXB annotations to support XML serialization, and the serialized representations contain links. In our implementation, each representation class inherits from a common parent that stores named links and deals with XML and HTTP metadata such as namespaces and media types. This base Representation class is shown in Example 5-22.

There’s only one JAXB annotation in the Representation class, and only one @XmlElement annotation to help serialize the links in the DAP namespace. The solution’s other representation classes (OrderRepresentation, PaymentRepresentation, and ReceiptRepresentation) extend the base Representation class, adding in links and business-specific information. The OrderRepresentation in Example 5-23 shows a typical implementation.

Much of the code in Example 5-23 is simply JAXB annotations. We use @XmlRootElement and @XmlElement to declare how to serialize fields into root and child nodes in XML. Aside from the framework code, though, OrderRepresentation instances are just value objects.

What’s more interesting about this code is the way it’s used by the workflow activities. Recall that representations are created by activities, and that activities know about subsequent valid activities. Using the constructor OrderRepresentation(Order order, Link…links), activities inject links into the representation. Those links advertise subsequent valid activities to consumers of the representation, informing them of the next steps to take in the DAP.

The workflow activity classes are units of work that execute some business interaction against the domain model on behalf of a consumer. Each activity knows about the valid activities that follow and is able to map those downstream activities to URIs, thereby rendering hypermedia representations for consumers. To illustrate, consider the create(…) method of the CreateOrderActivity in Example 5-24.

The create(…) method in Example 5-24 works as follows. On receipt of an Order instance for a given URI, we set the order status to UNPAID and attempt to store it in the order repository. If the order is successfully stored, we take the internal identifier generated by the repository and use it to compute both a public URI for the order resource and a corresponding URI for the payment resource.[58] We then create a new OrderRepresentation that contains the updated order information and the valid DAP links. This representation is then returned to the calling JAX-RS code and dispatched to the consumer. For a newly created order, we return four links marked up with appropriate rel and mediaType attributes in an OrderRepresentation instance:

rel=“http://relations.restbucks.com/cancel”

The operation requires the order URI, but no media type declarations, because the cancel operation uses DELETE.

rel=“http://relations.restbucks.com/payment”

The operation requires a Restbucks payment representation in the entity body to be transferred via POST to the payment URI.

rel=“http://relations.restbucks.com/update”

The operation needs a Restbucks order representation in the entity body to be transferred by POST to the order URI.

rel=“self”

The operation requires an order URI, with no entity body, and is invoked via GET.

In the other activities (ReadOrderActivity, UpdateOrderActivity, RemoveOrderActivity, PaymentActivity, ReadReceiptActivity, and CompleteOrderActivity), the pattern is repeated: domain objects are created from any input representations and the activity orchestrates an interaction with the underlying domain model. On completion, activities generate a response representation (if any is needed) and insert links advertising next valid steps in the protocol, along with any media type declarations needed to advance through those steps.

The consumer-side architecture for a hypermedia service is shown in Figure 5-11. In this stack, the Jersey client library provides HTTP connectivity and is responsible for mapping HTTP requests and responses into higher-level abstractions such as domain objects and exceptions, and dispatching them to workflow activities. The workflow activities process business payloads while actions handle hypermedia controls. Overall control of the consumer resides in the business logic, which uses the actions to orchestrate interactions with the service through the workflow activities.

Figure 5-12 shows how activities are again at the core of our architecture. Activities take business objects and use them to create representations to transfer to the service. For responses with representations, activities provide access to the business payload in the received data.

Importantly, activities also surface actions to the business logic—abstractions that correspond to future legal interactions with the service. Actions encapsulate the hypermedia controls and associated semantic context in the underlying representation, allowing the consumer business logic to select the next activity in the workflow.

Activities also encapsulate error responses from the service, providing a choice of actions to the consumer business logic. For example, if the service responds with a 500 Internal Server Error message when trying to place an order, the PlaceOrderActivity will yield a retry action—the only valid thing to do at that point in the protocol.

Implementing the consumer is nontrivial in the absence of frameworks that understand hypermedia. Nonetheless, with a few simple patterns, we can confidently build consumers for hypermedia services. To illustrate, we’ll walk through the code in Example 5-25.

When building a consumer, we know the service’s contractual information is contained in the media types and link relations the service supports, as well as in any entry point URIs the service provider chooses to share with us. Because we already know the processing model for the Restbucks media type, building a consumer for services that support that media type is straightforward.

The orderAndPay(…) method in Example 5-25 shows an ordering and payment implementation that we used in the functional testing of our Java service. The method takes the well-known Restbucks ordering URI (http://restbucks.com/order) and uses a PlaceOrderActivity to create and send an order representation to the service via the underlying Jersey client library.

Assuming the order is successfully lodged, the placeOrderActivity instance will contain a local Order object for the consumer to process and an Actions object that encapsulates the legal next activities (if any). Under the covers, the creation of an Actions object is parameterized from the link relations that the consumer plumbing finds in response representations.

From the set of actions returned, the consumer-side business logic can make choices about what to do next. For example, if the consumer discovers an UpdateOrderActivity in the Actions instance (using the actions.has(UpdateOrderActivity.class) call), it retrieves it by calling the actions.get(UpdateOrderActivity.class) method and uses it to update the corresponding order resource on the service.

From this point, the workflow proceeds through the remaining activities, updating (or canceling) the order, paying for the order, obtaining a receipt, and acknowledging receipt of the drinks. At each stage, the consumer follows the same pattern: look for the most desirable action to take at the current instant, execute it, and repeat.

Like Java, the .NET platform has frameworks, such as Windows Communication Foundation (WCF), that make working with HTTP more pleasant and productive. Again like Java, there’s no obvious framework for building hypermedia-aware services, so Restbucks developed one.

The Restbucks framework decouples hypermedia from business activities, and transparently maps between the service implementation and the DAP advertised to consumers. Figure 5-13 shows a logical view of how a service processes incoming and outgoing resource representations. A similar approach can be used to build consumers with the same framework.

In our solution, the network layer deals with HTTP and is provided by the .NET Framework. The hypermedia layer deals with resource state transitions. For incoming representations, it determines whether the request is valid given the current resource state. For outgoing representations, the hypermedia layer injects <dap:link> elements, which advertise the next legal protocol steps, into the response. The business layer, meanwhile, focuses exclusively on application logic and its data.

The hypermedia framework manages resource state transitions by embedding appropriate hypermedia controls into a response based on both the state of the resource targeted by the request and the state of any associated resources. To express this protocol, we have devised a simple, declarative Domain-Specific Language (DSL). Scripts written with the Restbucks DSL choreograph numerous business actions and externalize application state to consumers.

The hypermedia framework and its scripts are hosted on a web server implemented with the HttpListener class.[59] The server delegates incoming requests to the hypermedia framework’s dispatcher. The dispatcher maintains a collection of the currently active state machines, each of which runs an instance of our DSL program.

Each state machine analyzes incoming HTTP requests and, depending on the state of the resource to which the request relates, dispatches the payload to an appropriate .NET method in the business logic layer. The method deals with XML documents as the input and output, and is unaware of the HTTP and hypermedia details of the interaction.

On the response path, the hypermedia framework receives XML documents from the business layer and augments them with any hypermedia constructs declared in the state machine DSL description. The resultant payload is then passed to the underlying HTTP infrastructure for delivery to the consumer, as we see in Figure 5-14.

The DSL used for the Restbucks hypermedia framework is a state machine description markup language. This language includes constructs representing hypermedia controls, which are injected into representations as XML <dap:link> elements.

The DSL in Example 5-26 declares the possible states of a Restbucks order resource and the supported transitions from each of those life-cycle states. It’s easy to see how tooling could be used to produce such DSL documents, but we think it’s easy enough to write by hand too. While the primary focus of the DSL is to describe a resource’s state transitions, it also includes those DAP-specific transitions that relate to the particular resource.

The state machine description in Example 5-26 consists of a declaration of a set of global properties and a series of states through which the order resource may transition. The first state lexically is considered to be the initial resource state.

There are two different subconstructs in each state of Example 5-26:

  • The HTTPVerb is used to identify valid incoming HTTP requests, given the current state of a resource and what action should be performed upon receipt of such requests. The MethodName specifies the .NET method to dispatch followed by the HTTP status code of the response should that method complete normally. An optional => StateName identifies the state to which the resource should automatically transition after successful processing of the incoming request. The optional When conjunction allows us to deal with exceptions that the business logic from the invoked .NET method might generate, and the HTTP status code of the response in such a case.

  • The Links section declares the <dap:link> elements that should be included in the payload of the responses to consumers when the resource is at that particular state. The links can point to other resources hosted by Restbucks, or any other resources on the Web.

Using a declarative description of resource state transitions, such as the one shown in Example 5-26, we can modify and evolve a DAP without having to radically change our service’s implementation. Most importantly, however, we can rapidly develop and deploy new DAPs.

When the Restbucks service receives an HTTP request, it routes it to the hypermedia framework dispatcher, which in turn delivers it to the appropriate state machine instance. The dispatcher checks the request’s URI against each state machine’s URI template to determine which state machine should deal with the request.

The order resource state machine expects the first request to be a POST, as per the initial state of the DSL script in Example 5-26. By activating the initial state of a resource, the framework also creates an instance of the state machine to track the resource’s life cycle and its hypermedia interactions. The value of the UriTemplate property, up to but not including the /{id} variable part, is considered the URI for the initial request (http://restbucks.com/order). From that point on, requests matching the resource’s URI template are checked against the same state machine instance.

If an incoming request doesn’t match any of the expected HTTP verbs in the active state, the framework automatically responds with a 405 Method Not Allowed status code. Example 5-27 shows an initial POST request[60] to the .NET ordering service.

The web server receives the request and hands it to the dispatcher, which determines from the URI http://restbucks.com/order that the order state machine should process the request. Since the URI template and the verb match the conditions for the initial state and the media type is as expected, the state machine extracts the payload from the request and gives it to the NewOrder() method, as per the POST NewOrder definition.

Example 5-28 shows the NewOrder() method. The signatures for methods such as this, which handle requests for the initial state of a state machine, differ from those of the methods for the remaining states in that they return a string that acts as the identifier for the state about to be instantiated. The same string will also be used as the value of the {id} part in the state machine’s URI template. The framework maintains an internal dictionary of all the active states based on the unique key returned by the NewOrder() method.

As Example 5-28 shows, the hypermedia framework abstracts the HTTP details from the implementation. The application logic only needs to deal with XML payloads for the incoming requests and outgoing responses.

In our service, we create a new business object to represent the order in the database, serialize it back to XML, add it to the response, and return its identifier. The service will embed more information about the order (e.g., its preparation status and cost) in the payload. Remember that the hypermedia framework will add the necessary <dap:link> elements.

The response for this interaction has a 201 Created status code, as shown in Example 5-29, in accordance with the state machine definition in Example 5-26. The payload is the representation of the order resource. This representation includes <status> and <cost> elements, as well as hypermedia links representing the possible transitions the customer application can initiate. Notice the => Unpaid part following POST NewOrder 201 in Example 5-26; this tells the hypermedia framework to transition the state machine instance for the order to the Unpaid state once the response has been sent.

Note

In Example 5-29, the link for payment given to consumers is http://restbucks.com/payment/1. Remember: consumers should treat this as an opaque URI since they have no way of knowing that the URI path ending in /1 represents an internal identifier for the resource.

In this initial state, the order representation contains links that indicate that the customer can get the latest version of the order resource, or update it, or cancel the order, or submit payment. The verbs and the format of the payloads to be used are captured by the rel and mediaType attributes of each <link> element, as per our description of the Restbucks DAP.

Our next task is to add the payment logic. Without payment, the order won’t transition to the Preparing state or be given to the barista. From the moment an order is created, payment is expected. We treat payment as a separate resource, with its own internal state machine. The payment resource state machine is shown in Example 5-31.

Notice that there’s no service logic associated with the initial state. The hypermedia framework will not call a method to create a payment resource. Instead, it is expected that the resource will be created out of band, in code. Therefore, once an instance of the state machine is created, the framework will automatically transition it to the PaymentExpected state.[61]

Because consumers have already been given a payment URI, we have to follow a different pattern here. This requires a consumer to submit a PUT request containing the payment representation, which results in the PaymentReceived() method being called. The implementation of PaymentReceived() is shown in Example 5-32.

When the service receives a payment request, it creates a business object from the payload, and then retrieves the order from the database and sets its Status property to Preparing. The service then adds the payment to the database and initiates the preparation of the order. This last step starts an internal process involving the barista. To do this, the service calls ThreadPool.QueueUserWorkItem() to add the work to a queue. The barista receives the order information, prepares it, and then notifies the rest of the ordering service that the order is ready by calling OrderPrepared(), which moves the order into the Ready state.

Notice that the OrderPrepared() method is able to interact with the hypermedia framework without involving a consumer. This allows Restbucks to transition the state machine to the Ready state by binding directly to the state machine instance, rather than through the external HTTP interface.

Recall that when the service receives a payment from a consumer, it replies with a 201 Created response. The hypermedia framework adds an order link to that response. In this context, the link indicates to the client that it is expected to GET the latest representation of the order to find out whether it is ready for delivery.

When the barista calls the OrderPrepared() method, the order transitions to the Ready state. This change in resource state causes different links to be added to the order’s representation the next time it is requested. This is handled by the hypermedia DSL snippet shown in Example 5-33.

The DSL here indicates that the receipt link will be included in the payload of any response if the order is in the Ready state. To complete delivery of the order, the consumer only needs to send a DELETE request, as defined by the semantics of http://relations.restbucks.com/receipt of the application/vnd.restbucks+xml media type, to the receipt URI http://restbucks.com/receipt/1. The DSL for the receipt resource is very simple and similar to that of the payment resource, as shown in Example 5-34.

When the DELETE request is received by the receipt resource, the ReceiveOrder() method is called, as shown in Example 5-35.

Like the other methods, the implementation here is very simple. We retrieve the order from the database, update its status, and add its representation to the response. Because the protocol has ended, the representation passes through the hypermedia framework untouched before being dispatched to the consumer.

With the addition of hypermedia, we’ve reached the pinnacle of Richardson’s service maturity model in Figure 5-15. While we still haven’t completed our journey—we have plenty more to learn about using the Web as a platform for building distributed systems—we now have all the elements at our disposal to build RESTful services.

There’s more to the Web than REST, but this milestone is important because of the significant benefits, in terms of loose coupling, self-description, scalability, and maintainability, conferred by the tenets of the REST architectural style.

All of this comes at a rather modest cost when compared to what we did to build nonhypermedia services. This is encouraging, since it means the effort required to build and support a robust hypermedia service over its lifetime is comparable to that associated with building CRUD services. It’s certainly a better proposition than tunneling through the Web.

We’ve now seen how hypermedia services are straightforward to design, implement, and test using familiar tools and libraries. And by augmenting representations with hypermedia controls, we’re able to project DAPs over the Web to consumers. We’ve also seen that computer systems (not just browsers!) use hypermedia links to build DAPs that model (dynamically evolving) business workflows.

From here onward, we’ll assume that hypermedia and RESTful services are the norm. In the following chapters, we’ll see how scalability, security, and other higher-order protocols such as publish-subscribe can work harmoniously with the Web. Read on!



[43] Most middleware solutions, such as WS-*, emphasize the opposite: imperative contracts presented upfront.

[44] A popular hypermedia format and the focus of Chapter 7.

[45] As more coffee shops bring their business to the Web, it’s conceivable to create a common application protocol and a vendor-agnostic media type (e.g., application/coffee+xml). Until such an event, we will assume that Restbucks has the monopoly on coffee on the Web.

[46] Media types can reference many schemas. When accepting the contract imposed by a media type, you’re indicating that you understand the associated schemas.

[47] We will discuss the Atom formats and Atom Publishing Protocol thoroughly in Chapters Chapter 7 and Chapter 8.

[48] In the WS-* stack, these contract elements are typically implemented using XML Schema, WSDL, WS-Policy, and BPEL or WS-Choreography, respectively.

[49] Examples of such processors already abound; these include Apache Abdera and .NET’s syndication types, both of which implement the Atom Syndication Format.

[50] IANA defines numerous top-level link relations that are broadly applicable across numerous domains. These relations aren’t bound to any particular media type or protocol, and can be freely reused in any service implementation with matching requirements.

[53] Since the media type maps entirely to the Restbucks business domain, all link relation values used in the application protocol are defined in its specification.

[54] This could be something as simple as a hash of the number of coffees and a secret key.

[56] Java API for RESTful Web Services; see http://jcp.org/en/jsr/detail?id=311.

[57] The path for the updateOrder(…) method is a combination of the root path for the resource (/order) and the local path for the method (/{orderId}).

[58] If Restbucks outsourced payment processing, instead of computing a URI for payment, we’d ask the payment provider for a URI.

[60] The XML payload was formatted for readability. The raw XML is unpleasant.

[61] It could be argued that PaymentExpected should be the initial state and that the PUT request would create the payment resource. However, the semantics of the initial state in the DSL are such that we expect an HTTP request to be sent to the URI template without the {id} part. Since the payment resource was already created through code, rather than due to a request initiated by a consumer, we need to instruct the hypermedia framework to perform this automatic transition without expecting an initial request.