designing a RESTBucks media-type
i've been spending the last several months experimenting w/ a repeatable methodology for expressing business processes as media types for dist-net implementations (aka "the Web"). this blog post is one of a series i've been doing over the last year or so as i attempt to improve upon these patterns and practices.
the RESTBucks example
the book REST in Practice uses a very effective demonstration app called RESTBucks to illustrate the authors' ideas on how to build RESTful solutions. the book is one of three (along with RESTful Web Services and The RESTful Web Services Cookbook) i highly recommend for those interested in applying the REST style. i like the 'RiP' book for a number of reasons. one of them is that the problem domain used for the example (RESTbucks) is both simple enough to grasp and complex enough to be non-trivial. IMO, this makes for a great teaching tool when discussing REST, Hypermedia, and dist-net architectures.
along with the code examples supplied in the book, i know of two additional RESTbucks implementations available right now: http://restbuckson.net/ and https://github.com/SzymonPobiega/restbucks-wcf. there may be more, too!
the hypermedia way
for this blog post, i've commandeered the RESTBucks examples from REST in Practice in order to illustrate a simple methodology for expressing application domain details as hypermedia types in a way that works for HTTP and the Web. keep in mind this is my own interpretation of the RESTbucks example and is not meant to indicate the endorsement or approval of the authors of REST in Practice.
the material that follows is based on content from a new book i am working on entitled "Building Hypermedia APIs with HTML5 and Node" that will be released later this year.
i currently approach the process of implementing RESTful solutions differently than the authors of REST in Practice. i favor crafting a media-type that can express the application domain, state transitions, and process flow as required. from my POV, this allows me to focus only on the message sent between parties and ignore the implementation details of any particular platform, framework, programming language, etc. in theory, this results in an 'cross-platform' design that can be successfully implemented using any framework. more over, the resulting client and server solutions should be able to successfully interact with each other regardless of the implementation deatils since they all are aimed at support the media type w/ high fidelity and are not concerned about the *way* in which it is expressed (e.g. the implementation details specific to any language, etc.).
the Methodology
in its current form, my methodology for designing a hypermedia type for an identified problem domain has four key parts:
- Select a Format (XML, JSON, HTML, etc.)
- Select a Domain Style (Specific, General, Agnostic)
- Select a State Transition Style (Read-only, Predefined, Ad-Hoc)
- Select a Process Flow Style (None, Embedded, Applied)
since this is a blog post, i won't dwell to much on the above list except to say that, for the RESTbucks example, i have settled the following for my design:
Format | XML | it's the style used for the RESTBucks examples in the book. |
Domain Style | Specific | it's similar to the style used in the book |
Transition Style | Predefined | the data supplied for each transition is spelled out in the docs and does not seem to require much flexibility |
Process Flow Style | Embedded | the process flow is well-established in the docs and can be mapped directly to @rels within the format |
all these design choices are arbitrary and are driven mostly by my desire to keep the example in this blog relatively clean and simple.
once the design choices are made, there is the work of identifying the states that need to be represented and the transitions that need to be supported. these are found by scanning the narrative of the problem domain just as in any other process of translating 'real-world' use cases into something developers can convert into computing services.
the States
first, let's consider the states that need to be expressed for the RESTbucks example. i've settled
on two for this illustration: the set of orders(list
), and a single order (detail
).
these two states will need to be expressed in a document. here's a simplistic example:
<rest-bucks version="1.0"> <order-list> <order> <status /> <location /> <cost /> <items> <item> <name /> <quantity /> <milk /> <size /> </item> ... </order> ... </order-list> </rest-bucks>
you'll notice that this document can express both a set of orders and a single order. you'll also notice there are no links in this representation. i left them out for clarity (and effect[g]).
the Transitions
a cursory review of the RESTbucks scenario leads me to identify the following state transitions:
- get a list of orders
- get an order detail
- place an order
- cancel an order
- update an order
- pay for an order
- accept an order
- reject an order
- prepare an order
- deliver an order
i may have missed a few or mis-represented some details of the transitions, but i think you get the idea.
so the RESTBucks representation i have in mind now has a number of possible links:
<rest-bucks version="1.0"> <link rel="place" href="..." /> <link rel="list" href="..." /> <order-list> <order> <link rel="detail" href="..." /> <link rel="update" href="..." /> <link rel="cancel" href="..." /> <link rel="payment" href="..." /> <link rel="accept" href="..." /> <link rel="reject" href="..." /> <link rel="prepare" href="..." /> <link rel="deliver" href="..." /> <status /> <location /> <cost /> <items> <item> <name /> <quantity /> <milk /> <size /> </item> ... </order> ... </order-list> </rest-bucks>
first, note that some links appear at the top of the document (list
and place
)
and some appear only within a single order
element. that means some of these links could
appear multiple times within the same response.
also, not all the links shown above would appear in a single representation. but certainly more
than one of them could appear in the same representation. for example, once an order has been successfully
created, the representation sent to the user who created the order would likely include the following
transition options:
- list
- detail
- update
- cancel
- payment
you'll also notice that none of the links contain actual URIs. the value of the href
is of no
consquence to clients; they will treat the URI as an opaque string. this allows each individual server
implementation to chose the URI details that best fit that implementation. many frameworks are rather
restrictive in how URIs must be constructed in order for servers to properly route and process the
requests. quality media type designs allow servers to do whatever is necessary in order to
handle the request properly.
representing the Transitions
there are a number of ways to represent the actual state transitions. some of them are simple links. others require added parameters; they could be viewed as 'link templates' or 'forms.' in my current experience, the act of deciding on how the state transitions are rendered in the responses is one of the most contentious aspects of design for a dev team. there are those who want to use existing technologies to express 'inline' state transitions (HTML FORM, XFORM, etc.). there are some who want to use these same technologies but keep the transition representations as separate resources (ala Rails). there are some who want to design custom payloads using XML, JSON, etc. and simply document them w/o passing the details in the responses (ala Atom).
in my mind, these are design choices that people are free to make. as long as the work is consistent and the team agrees to the pattern, there is no appreciable advantages or drawbacks to adopting one of these patterns over the other.
NOTE: for the sake of brevity here, let's assume all successful state transitions return a response that is one of the following:
- an order list representation (200 OK)
- an order detail representation (200 OK)
- a redirect to either an order list or order detail representation (201 Created, 3xx)
with the above as preface, here is my list of transition representations. in this case, i've used XML link
elements w/ annotations of the possible HTTP protocol methods. this format usually is enough to express the key
points to just about any developer group and lead them to crafting a final representation upon which they all can
agree.
<!-- return a list of orders (HTTP GET) --> <link rel="list" href="..." /> <!-- get a single order detail (HTTP GET) --> <link rel="get" href="..." /> <!-- place a new order (HTTP PUT/POST) --> <link rel="place" href="..." > <name /> <quantity /> <milk /> <size /> </link> <!-- update an existing order HTTP PUT/POST --> <link rel="update" href="..." > <name /> <quantity /> <milk /> <size /> </link> <!-- cancel an existing order (HTTP DELETE) --> <link rel="cancel" href="..." /> HTTP DELETE <!-- OR (HTTP POST) --> <link rel="cancel" href="..."> <order-href /> </link> <!-- pay for an existing order (HTTP POST/PUT/PATCH) --> <link rel="pay" href="..."> <account-number /> <amount /> </link> <!-- accept an existing order (POST/PUT/PATCH) --> <link rel="accept" href="..."> <order-href /> </link> <!-- reject an existing order (POST/PUT/PATCH) --> <link rel="reject" href="..."> <order-href /> </link> <!-- prepare an existing order (POST/PUT/PATCH) --> <link rel="prepare" href="..."> <order-href /> </link> <!-- deliver an existing order (POST/PUT/PATCH) --> <link rel="deliver" href="..."> <order-href /> </link>
you'll note that some of the transitions suggest more than one possible protocol method. the choice would be dependent upon what state information the client has available and what the server supports. it's not likely that all of the possiblites would be valid, but it gives folks a chance to consider the alternatives and weigh the pros/cons of each.
the details
there are lots of missing details here. data types, when certain transitions are valid/invalid, error-handling and representation, access controls, which 'actors' in the domain can 'see' or 'do' which transtions and when, etc. there may even need to be more states and transitions defined in order to have a more 'complete' expression of the problem domain.
but these short-comings are relatively easy to resolve. additional iterations through the problem domain, adding/removing elements, attributes, etc. as needed, and so forth.
the code
oh yea, the code...
once the media type (message format and content) is designed, servers and clients are free to do whatever work is need to "support" this media type. both parties (client and server) know what a valid document looks like, what links could possibly appear in a response representation, what parameters are needed when executing a state transition, etc.
different platforms, frameworks, etc. will mean different implementation details; that's fine. as long as both parties continue to focus on the message format as the 'sole arbiter' of compliance, things will work out just fine. IOW, the media type is the 'contract' to which both parties adhere. in my experience, this approach to the implementation step is a bit disconcerting to the dev team.
"You mean there are 'no rules?' We can do it however we wish? But what about proper URIs and agreed upon classes and objects and all that?"
once they start to actually code servers that emit valid messages and clients that recognize and render state transition elements, dev teams usually get pretty charged up. it turns out to be 'easier than first thought' to code using a media type as the contract. server devs have the freedom to use whatever components and technologies they wish. they can even change these elements at any time as long as the server continues to emit valid messages. clients can be as creative as they wish in rendering the responses. if the target client base is Web browsers, devs can start with simple HTML renderings, add support for async Ajax, switch to RIAs, etc. all as long as the client is implemented to always accept and process valid messages.
even more interesting, as time goes on, teams have the freedom to test new UIs, optimize servers in new ways, etc. without worry of upsetting the application ecosystem; as long as all particpants stay focused on maintaining high fidelity support for the agreed media type|messages.
and the point here is...?
the point of this exercise is to give folks some ideas on how to approach hypermedia implementations from a new perspective; that of the hypermedia messages passed between parties. not the private classes/objects, not the URIs, but the messages and the hypermedia within those messages.
my experience to date is that this approach works regardless of the technology used by a team. it also often makes implementing hypermedia clearer, easier to grasp by all participants and, in many cases, results in a more flexible and stable solution that can safely evolve over time.