Posts Tagged ‘hateoas’
Not yet REST
How do we achieve REST? Leonard Richardson’s model was widely commented and Martin Fowler posted on “Rest in Practice” (a book I recommend reading). But what is left out from REST in Richardson’s model and why?
According to his model, level 3 adds hypermedia support, leveraging a system through the use of linked data – a requirement for a REST architecture. But HATEOAS alone does not imply in REST, as Roy stated back in 2008.
Remember how method invocation on distributed objects allowed you to navigate through objects and their states? The following sample exemplifies such situation:
orders = RemoteSystem.locate().orders();
receipt = order.payment(payment_information);
But what if the above code was an EJB invocation? If navigating through relations is REST, implementing EJB’s protocol through HTTP would also be REST because linked data is also present in EJB’s code – although lacking an uniform interface.
While Richardson’s model get close to REST on the server side, Rest in Practice goes all way to a REST example, describing the importance of semantics and media type importance. The rest of the post will explain what was left out of this “Rest services” model and why, proposing a model that encompasses REST, not REST under http; while the next post, with a video, describes how to create a REST system.
What is missing?
Did the previous code inspect the relations and state transitions and adapted accordingly?
It did not choose a state transition, it contains a fixed set of instructions to be followed, no matter which responses are given by your server. If the API in use is http and the server returns with a “Server too busy” response, a REST client would try again 10 minutes later, but what does the above code do? It fails.
We are missing the step where REST clients adapt themselves to the resource state. Interaction results are not expected as we used to in other architectures. REST client behavior was not modelled on Richardson model because the model only thought about server side behavior.
This is the reason why there should be no such a thing as “rest web services” or “rest services”. In order to benefit from a REST architecture, both client and server should stick to REST constraints.
Richardson’s server + http model
Semantic meaningful relations are understood by the client, and because of that we need a model which describes how to create a REST system, not a REST server.
An important point to note is that this model is pretty good to show a REST server maturity over HTTP, but limiting REST analysis both to server and http.
A REST architecture maturity model
For all those reasons, I propose a REST maturity model which is protocol independent and covers both consumer and provider aspects of a REST system:
Trying to achieve REST, the first step is to determine and use an uniform interface: a default set of actions that can be taken for each well defined resource. For instance, Richardson’s assumes HTTP and its verbs to define a uniform interface for a REST over HTTP architecture.
The second step is the use of linked data to allow a client navigate through a resource’s state and relations in a uniform way. In Richardson’s model, this is the usage of hypermedia as connectedness.
The third step is to add semantic value to those links. Relations defined as “related” might have a significant value for some protocols, but less value for others, “payment” might make sense for some resources, but not for others. The creation and adoption of meaningful media types allows but do not imply in client code being written in a adaptable way.
The fourth step is to create clients in a way that decisions are based only in a resource representation relations, plus its media type understanding.
All of the above steps allow servers to evolve independently of a client’s behavior.
The last step is implied client evolution. Code on demand teach clients how to behave in specific situations that were not foreseen, i.e. a new media type definition.
Note that no level mentions an specific protocol as HTTP because REST is protocol independent.
The following post will describe one example on how to create a REST system using the above maturity model as a guide.
Due to the last posts on infoq related to Restfulie, my work at Caelum Objects involved a presentation at one client, “Beginning a REST initiative” (based on Ian’s work) and the question came up: “but how do I control transactions without a custom software stack to help me?”
The answer was, “you do not need to”.
Restwiki has an old entry on how to implement transaction support through http using some non-standard http headers.
In practice most ideas are based on a transaction being a resource named “Transaction”: an idea heavily based on HTTP and URIs, but forgetting about HATEOAS – again.
In the human web, how does one buys some products? Every product is added to the shopping basket, which then generates the order. Does the user creates a transaction before processing his order?
The human being behing the computer did not create a transaction: the browser is even unaware of that concept, but hyperlinks given by the server guided the client through this “transaction”. In this case, where the typical “REST” solution would create a “Transaction” resource and use the non-standard header to support it, a Restfulie one creates a shopping basket:
|Typical “REST” approach||Restfulie|
POST /product *
POST /product *
|commit||POST /transaction/commit *||PUT /basket/:id/payment|
|rollback||DELETE /transaction/ *||DELETE /basket/:id|
* with non-standard http header
The standard way of thinking about transactions is to not use HATEOAS and believe that transactions are resources by themselves. Transactions are not resources, but a tool to implement ACID in your (i.e.) databases, not in a web system.
In our example, an order creation maps to internal transactions. In a bank example, a Transfer resource would map to the internal transaction.
By renaming the “transaction” to the real objective of that transaction, one can better map meaningful URI’s to resources.
Note that these are only the advantages of valuing the use of URIs over non-standard http headers (manifest hint?): there is no loss of visibility to layers between the client and the server.
But now one might argue that there are too many entry points. Actually, both implementations contain the same number of “entry” points if there is no hypermedia support: 4. Too many entry points should not be called “entry” points. (entry-hell pattern?)
But do we, in the human web, type in URIs as we go further with our online “transaction”? Do we type in URIs as we do a two-step flight and hotel booking process?
If the entry point POST /basket answers with a:
<link rel="products" href="http://caelumobjects.com/basket/5/products" />
<link rel="coupon" href="http://caelumobjects.com/basket/5/coupon" />
<link rel="pay" href="http://caelumobjects.com/basket/5/payment" />
<link rel="cancel" href="http://caelumobjects.com/basket/5" />
Note that our basket – our transaction’s meaning – contains hints on how to operate with it and its relations pretty much in the same way that it would do in the human web: dynamically generated links that allows the server to guide the client throughout the process, eliminating the need to extra “entry-points”.
In a hotel and flight booking system, the booking POST result could be represented as:
<link rel="flights" href="http://caelumobjects.com/booking/5/flights" />
<link rel="hotels" href="http://caelumobjects.com/booking/5/hotels" />
<link rel="pay" href="http://caelumobjects.com/booking/5/payment" />
<link rel="cancel" href="http://caelumobjects.com/booking/5" />
Note how the first idea on implementing transactions evolved. From a custom header which interferes with visibility and creates the need for custom built clients and layers to understand this instruction, with no server guidance at all to a system where there is no need to customize your client api or layers and the server guides the user flow through hypermedia, maturing your system.
Transactions should not be called “transactions”. The basket or transfer resource are examples of that: they are typical server side implemented transactions that should be actual resources.
Our basket (and thus transfer) seems to match Roy’s comment at that time:
- “As far as the client is concerned, it is only interacting with one resource at a time even when those interactions overlap asynchronously.”: the basket or the transfer
- “There is no “transaction protocol” aside from whatever agreement mechanism is implemented in the back-end in accordance with the resource semantics (in a separate architecture that we don’t care about here).”: you add products to the list of products form that basket, add some coupons and so on
- “There is no commit protocol other than the presentation of various options to the client at any given point in the application.”: hateoas
- “There is no need for client-side agreement with the transaction protocol because the client is only capable of choosing from the choices provided by the server.”: transaction protocol? no transaction protocol here, just a simple resource handling
Restfulie – as many other rest frameworks -already support the first step (running away from the custom header), but goes further when being “hypermedia centric”, it allows the developer to implement it without any effort.
Being opiniated and forcing the adoption of hypermedia as a way to guide or clients through out our processes might be one step ahead into more web (rest in this case?) friendly world as Ryan Riley pointed out.
HATEOAS, HTTP and URIs allow you to eliminate the concept of transaction management (and web transaction managers) from your systems as we usually think of them. There are two steps to follow:
1. there are no transactions
2. let the server guide you, do not try to guide him with multiple entry points