In an exclusive nine-part dialogue with an imaginary eBay Architect, we present an accessible discussion of the REST vs. SOA issue.
Although eBay have what they call a 'REST' interface, it is, in fact, a STREST interface, and only works for a few of the many function calls that they make available via SOAP (GetSearchResults, GetItem, GetCategoryListings, etc).
In this dialogue series, I argue the case for eBay to adopt a truly REST approach to their integration API.
Part 3: Business Functions
Duncan Cragg: So, where did we get to? Oh yes: the REST recipe for scalable and interoperable distributed systems!
We can read data at a URI with GET. We will usually understand that data when we get it, because it has a standard content type at a number of layers - perhaps from character set up to Microformat via XML and XHTML.
We can cache the data if the response allows it.
Then we can POST back our own content to the same URI - if we believe that this resource is interested and that it will do something we want.
Finally, the content we GET has more URIs that we can use in the same way.
eBay Architect: Simple enough.
DC: In fact, these are essentially the constraints of REST as formulated by Roy Fielding. Note that Fielding hardly mentions actual verbs in his thesis: he, in fact, gives special mention to GET and PUT, which is consistent with the basic 'state transfer' concept - one verb for each direction. The Web (and I) prefer POST to PUT!
Although we expect the resource to change in some way - perhaps change, create more resources or delete resources, the resource can actually do what it likes when POSTed to, as long as it conforms to its declared standard.
Don't forget that REST doesn't require direct resource editing as either a necessary or sufficient mode of interaction, and that in general a resource can do what it likes on receipt of incoming content, as long as both sides have agreed in advance.
eA: How do you know what a resource will do, then?
DC: The standard to which the POST target conforms will declare the type of the POSTed content and set the POSTer's expectations of the consequence of such a POST.
HTML, of course, is a bit vague and low-level about what you can POST - it's either name/value pairs or files, with no promise about what will happen afterwards. This is because a human is directly involved in the interaction. In contrast, we're discussing the general REST integration case here, where higher-level, more complex, machine-generated POST types are expected.
eA: This all sounds fine to me. But, like I said, our API has more complex business functions in it that go beyond simple data read and write.
DC: What sort of functions were you referring to?
eA: There's many of them. Functions like PlaceOffer, RespondToBestOffer, CompleteSale, SendInvoice.
These are real functions, not getting, setting or adding data.
DC: Indeed, and as such form an excellent exercise to demonstrate the general power of REST!
eA: Show me how. What's the trick?
DC: The trick is simply to identify your resources, then to discover or define how their state depends on related resources and POSTed content, using transformation rules, or 'animation' code.
Indeed, if the state of a resource depends on the state of other resources - or POSTed data - via a transformation, then you have a complete, general programming model.
eA: You mean my 'real functions'?
DC: Yes - a resource can do any 'real functions' just by watching resource changes via GET or receiving resource data via POST on its URI and then mechanically transforming itself according to its internal rules - as defined by standard, convention or agreement.
Data operations are enough to enable much more than just data operations, as long as you have the internal transformation rules in place that animate the data in the face of current state.
eA: What does this mean to an eBay Architect? How does that help with placing offers and responding to offers? How do I send an invoice?
A REST eBay Transaction
DC: OK, first identify your resources: let's look at what resources are implied by your list. You mentioned PlaceOffer, RespondToBestOffer, CompleteSale, SendInvoice.
So, it looks like we have this list of resource types: User, Item, Offer, ResponseToBestOffer, Feedback, Invoice.
Notice how, when we go from function (RespondToBestOffer!) to resource (ResponseToBestOffer), we change the names from an imperative instruction style to a declarative state style. This is a crucial change in mind-set.
eA: Um - the CompleteSale function isn't just for adding feedback, it allows paid and shipped to be set on an Item.
DC: Exactly: paid and shipped status are attributes of the actual Item being sold; the Item would be updated directly to complete a sale. However, it makes sense to put Feedback in its own separate resource.
Admittedly, this is just my initial analysis. I'm sure it could get more complex on closer inspection; I'm just trying to be illustrative. And I don't necessarily understand the whole eBay auction process, so please forgive any slip-ups there!
eA: OK. Now you said that we should define how these resources depend on each other and on POSTed content, using transformation rules. Take me through that.
DC: Sure. There are two API Users: Sam the Seller and Bill the Buyer.
Sam POSTs his Item resource, and eBay's servers create a linkable copy server-side, returning the URI in the 'Location:' header of the POST response.
Note that a server doesn't always need to create a linkable copy of what's POSTed to it, but that's the pattern I'll use in these examples.
Let's say Sam the Seller is posting a Fixed Price Item with Best Offers switched on - since you've mentioned RespondToBestOffer. Here, people can suggest a near, lower offer and a bit of negotiating can take place. Not the usual eBay auction but a simpler interaction that is perhaps more generally-applicable in e-commerce.
eA: Let Bill do a PlaceOffer, then: any transformations now?
DC: Yes. A number of people, including Bill, POST Offers linking to the Item and see these Offers created on the server.
eA: So how does Sam get to know about the Offers on his Item?
DC: Ah - that's your first transformation! Each time an Offer is POSTed referring to an Item, the target Item itself is updated. In particular, the Item has a sub-container with a list of current Offers on it, ordered by value.
At any time, anyone can fetch their own resources, and some owned by others, with a GET. Sam can GET his Item and its list of current Offers; Bill can also see the Item, and GET his Offer. Items and Offers thus refer to one another by internal links that everyone can understand because they understand URIs.
eA: Is this transformation really that big a deal?
DC: Not hugely. This transformation rule encapsulates the commonly-occurring concept in commerce of a range of values of multiple bids on some sale item - such as found in real estate and in financial markets.
A further business transformation rule may perhaps have Offers running a 'best-offer' status flag that is kept consistent with that Offer's relative state.
As you can see, and as is often the case in REST and declarative programming generally, these business rules are very simple.
eA: So, Sam sees a number of Offers. Let's say Bill has the highest and Sam wants to take it.
DC: Sam then POSTs a ResponseToBestOffer, accepting, and linking to, Bill's Offer. Again, Sam gets the server-side URI of his ResponseToBestOffer for future reference.
eA: This is where I'd call the RespondToBestOffer function.
DC: Yep. Now, you have this state: an Item from Sam and a list of Offers on that Item, with a Best Offer from Bill. Onto that overall state arrives Sam's ResponseToBestOffer referring to and accepting Bill's Offer.
This new state is 'in tension': it's not yet mutually self-consistent with the business rules - which say that we have a sale.
To resolve the tension, something's got to change. So all the losing Offers get updated to 'lost', and Bill's to 'won'. The Item gets updated to 'sold', with a link to Bill's Offer.
Now we're back in harmony again after some simple transformations on our resources.
eA: Mm. That's an interesting perspective.
DC: Yes, instead of calling functions, we're asserting state and then applying rules to bring the state into a configuration consistent with those rules.
eA: OK. So let's go back to my SOAP function list: CompleteSale. You said paid and shipped from CompleteSale are on the Item - but they still need to be actioned. And we still need to add Feedback.
DC: Clearly, you can set the paid and shipped status of an Item using POST to the Item's URI, just like in the simpler data setting examples before.
Always remember, though, that an Item is responsible for its own integrity - it may change spontaneously according to its internal transformation rules, or may change directly via User POSTs - but only if it will be consistent with its internal integrity rules.
DC: Adding Feedback happens by the seller POSTing a Feedback resource to the URI of the buyer User's Feedback collection, which then returns a URI for the server-created copy.
A number of Feedback resources can be aggregated into a rating on a User resource; another simple transformation, where the rating is dependent on the state of the collection of Feedback resources.
eA: That's a point - what URI did you POST your Items and Offers to before, to get them created on the server?
DC: A seller User can keep a collection of current Items to POST to; a buyer User can keep a collection of current Offers.
It's just like in the Atom Publishing Protocol: new Items are POSTed to the seller's Item collection, new Offers either to a buyer's Offer collection or directly to the relevant Item - either way should cause the Offer to be added to the other Offer list.
eA: OK. SendInvoice? This has the side-effect of sending an email - now there's an imperative!
DC: Not when you think declarative!
To send an Invoice, just POST it to the buyer User's Invoice collection to get it created on the server with its URI.
Creation of the Invoice resource then triggers notification to the recipient by email. This email can either include an entire copy of the Invoice, or have a nice RESTful link back to the actual Invoice resource.
Email notification in a REST perspective falls into the same category as Web Feeds and POST - it's a proactive way to get resource state or state change out to an interested party.
Note that, like all interactions in this approach, it's not event-driven - it's state-driven. If you keep re-POSTing the same Invoice, or Item or Offer, it only gets created once, and the email only needs to be sent once!
eA: All these resources floating around - how do you manage them? You mentioned the Item and Offer collections.
DC: A seller's User resource would collect resources together like Items and ResponseToBestOffers. A buyer's User resource would have links to their collections of Offers, Invoices and Feedbacks. Buyers can also be sellers, of course.
These collections all form good candidates for viewing through a feed reader, or feed reading widget. For example, you could subscribe to your Item's collection of Offers to see new offers come in. You might subscribe to the Item collection of a seller whose goods often interest you. You would almost certainly have a subscription to the collection of Feedbacks about you...
This kind of thing falls naturally out of the REST approach, as long as collections have their own URI. Note that, unlike Web Feeds, such collections should contain links to their contents, perhaps alongside some summary information, not entire copies of their contents' text!
(c) 2006-2007 Duncan Cragg
In Part 4: Inter-Enterprise REST Integration.
Note that the opinions of our imaginary eBay Architect don't necessarily represent or reflect in any way the official opinions of eBay or the opinions of anyone at eBay.
Indeed, I can't guarantee that the opinions of our real blogger necessarily represent or reflect in any way the official opinions of Roy Fielding...