Re: ISSUE-31: Are Operations violating REST's uniform interface constraint?

On Feb 28, 2014, at 11:11 AM, Markus Lanthaler <markus.lanthaler@gmx.net> wrote:
>>> 
>>> I can't help, but to me this looks like a perfect example of RPC:
>>> 
>>> addItemToCart(productId, productVarientId, quantity)
>> 
>> How did you arrive at that?
>> If you want to look at it that way, the
>> sure. However, I see it more like this:
>> 
>> curl -X POST -H "Content-Type: application/ld+json" -d '{"@context":
>> "http://example.com/cart", "@type": "AddItemToCartRequest",
>> "productId": "1789", "productVarientId": "1641", "quantity": 2}'
>> http://example.com/my/cart
> 
> Because the first analogy that came to my mind was
> 
> <?xml version="1.0"?>
> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
>  <soap:Header>
>  </soap:Header>
>  <soap:Body>
>    <e:AddItemToCartRequest xmlns:e="http://example.org/shopping">
>      <e:productId>1789</e:productId>
>      <e:productVarientId>1641</e:productVarientId>
>      <e:quantity>2</e:quantity>
>    </e:AddItemToCartRequest>
>  </soap:Body>
> </soap:Envelope>

If you want to look at it that way, sure.

> 
> 
>> Sure, there's nothing stopping you from sending wrapping either of the
>> above in a function like you've described, but that function would
>> still have to format the message and send it over HTTP POST. I'm still
>> working with the uniform interface of HTTP. The only difference is that
>> the message being sent indicates intent. What I want to do is expressed
>> in the message rather than around it.
>> 
>>> In REST, you would much rather want to modify its state by PUTing a
>>> new representation.
>> 
>> Why must this always the case? As I've illustrated with the HTML forms
>> example already, this is not the model that HTML forms work with today.
>> Unless of course you disagree with the statement that HTML exhibits a
>> number of RESTful qualities.
> 
> It must not but if you want to benefit from the uniform interface you should
> use a method with some semantics. POST is a catch-all with no semantics.

What!? Are you serious? POST basically says “process this for me. “ It also states that because it may have side effects (i.e. changes the state of other resources), it likely shouldn’t be replied without confirmation. 

> Of course you can tunnel everything over a POST (as SOAP/WSDL did in its early
> days) but you lose a number of, sometimes important, aspects. If you PUT,
> and your request fails. You simply repeat it. If your POST fails, it's not
> obvious how to react. I don't know how many HTML forms I've seen that had
> some JavaScript code to prevent multiple POSTs.

Sorry Markus, but I think you’re take on POST are horribly misguided. POST has utility and semantics that are incredibly useful. If your view point is that POST is used for tunneling or that it’s SOAP like, I don’t even know if you should bother reading the rest of my response?

> 
> 
> 
> [...]
>>>> More importantly,
>>>> I know what the intent of the user is because they sent me a message
>>>> requesting to add the product to the cart as opposed to POSTing a
>>>> http://schema.org/Product instance to a URL.
>>> 
>>> POST is always tricky as it has so little semantics. If we would
>>> replace that example with PUTing a new shopping cart representation
>>> to the URL of my shopping cart, the situation would be quite different.
>>> The intent would be quite clear without requiring a special
>>> AddItemToCartRequest message.
>> 
>> I assuming that the interaction you're talking about is that the client
>> dereferences a representation of the current state of the cart. When
>> they want to add something to that, the modify the cart locally and
>> issue a PUT to replace the current state of the cart, correct?
> 
> Correct

Interesting. 

> 
> 
>> Assuming that's true, the server has some work to do to determine what
>> happened to the cart. Did I add an item? Did I change the quantity? Did
>> I change the color of the thing I ordered? Did I remove the item? And
>> "UpdateCartOperation" is just too vague and the expectations that a
>> client may have on this operation could yield unexpected results.
> 
> Does it have to know? It can simply discard the existing cart and replace it
> with a new one. You get the additional advantage that the complete cart is
> in the request which might be helpful if you need to scale your system.

Discarding the cart is an incredibly different scenario than modifying the carry for a specific purpose.

> 
> 
> [...]
>>> So you say sending an AddItemToCartRequest is preferable than PUTing
>>> a new ShoppingCart representation?
>> 
>> If I had my druthers, yes this is exactly what I'd do. And I'd send it
>> over POST and not PUT. You can imagine that by adding n items to my
>> cart, this may or may not have an affect on available inventory to
>> there potential shoppers. Perhaps my store will reserve the quantity in
>> your cart for some period of time until you check out? Since this
>> operation will now have side effects on other resources, say the
>> catalog and available inventory, PUT is not appropriate.
> 
> Why is PUT not appropriate in that case (if you send the complete shopping
> cart)?

Let’s frame this another way: I hope you’ll agree that a shopping cart is a specialized collection of things I might eventually buy. But at a base level, it’s a collection of things. Now let’s take the concept of a collection of things further and suppose that you have 400,000 items in that collection. Would you seriously deference the entire collection in order to modify it? I certainly hope not. 

But more to the point: there’s likely other resources that could be affected by the request. AMC Theaters here in the U.S. has a reasonable example of such a use case. In some of their locations, they do reserved seating. When you pick your seats, they are reserved to you for for 7 minutes. That means the available inventory to other users are affected, thus POST is a more appropriate HTTP method to be used. 

If AMC took your approach and dereferenced the entire state of the cart, modifying it, and then using PUT to save back, makes this kind of process impossible. The sever doesn’t see the interactions that the client is making and thus couldn’t reserve the seats for the 7 minutes. It simply wouldn’t work at all with this CRUD model. Yet, AMC has this working today using an HTML version of what I’ve described. 

To put it bluntly: REST != CRUD. If Hydra is approaching REST as it were a CRUD model, it’s going to have a difficult time achieving success. 

Like Mark, I’m now having my doubts about Hydra. 

> 
> 
>> This is pretty
>> much the same way HTML forms work today, but you're only dealing with
>> key/value pairs. But because I'm affecting other resources, I want to
>> use POST.
>> 
>> From a performance perspective, smaller messages tend be faster over
>> the wire.
> 
> Right, but HTTP/REST wasn't optimized for that at all. It is a
> coarse-grained hypermedia system. If you are concerned about performance,
> you should go a level deeper and operate on a TCP or UDP socket and send
> optimized (probably binary) messages.

First, see my point above. There’s hardcore optimization and dealing with super optimized data formats and more efficient data formats. And there’s common sense approaches to designing an application. 

Second, I highly encourage you to look at just about every HTML-based application and just attempt to abstract it from HTML and look at the conversation. If you squint and look at the messages being passed back and forth, you’ll see requests that declare intent and those messages are discovered at runtime. HTML, or REST for that matter, doesn’t prescribe this CRUD model. 

> 
> 
>> Using a PUT to update the entire cart would get slower
>> because now my cart grows with every addition and will continually get
>> bigger. Furthermore, I don't know why someone sent back their cart
>> using a PUT.
> 
> Because they want to replace their current cart with the new one!?

You do realize that carts typically contain things like:

* Total number of items
* Total cost of each item multiplied by quantity
* Total cost of the cart
* In some cases, taxes and shipping are calculated

Would the client have the necessary information to all of this without talking to the server? 

> 
> 
>> All I know is that they issued a PUT. I you haven't
>> guessed yet, I'm a bit more of fan of Domain Driven Design and CQRS. In
>> short, I don't like making my write model the same as my read model. I
>> also can optimize writes differently than I do for my write model.
> 
> What exactly do you mean by "write model" and "read model"? The data model?
> The way it is serialized? The way you implement it?

Okay, let me explain a bit:

A CRUD model basically uses the same entities for reads and writes. You dereference and entity, modify it, and put it back. There’s no notion of actions or operations because there are only 4. This totally works super great for simple applications or ones aren’t of major significance. In more systems with a more complex behavioral model, CRUD isn’t the best choice. Depending on my application needs, I may choose to work things differently. My system is more complex and I may choose to define some very specific actions that clients can use to interact with my system. 

By “write model” I’m saying the “action” data model I’m exposing to clients in order to modify the state of data in my system. I’m doing this by exposing to my clients, very specific messages or “forms.” This is pretty much the how HTML forms work today. The quintessential “add to cart form” is a perfect example as it doesn’t make the browser dereference the entire cart, but rather it presents the user with a means to construct a simple, yet concise message to add the desired item to their cart. It’s a few key/value pairs that describe the activity of adding to a cart. 

By “read model” I’m describing the thing that clients who are browsing my catalog are looking at or the state of their cart. When I issue the “Add to Cart Request”, I get back the new state of my cart. This activity could also affect other users in the application viewing the same area of the catalog and now the available inventory has changed. 

CRUD works for the simple things, but for more complicated systems it’s simply not appropriate. If you’re still curious about what I’m talking about, check out the intro to CQRS [1], it’ll give you a bit more perspective of where I’m coming from. 


> 
> 
>> But we're going off the rails a bit here, and I want to go back to
>> Mark's original point. This debate we are having go back to Mark's
>> DELETE/Clear example. What your solution seems to be advocating is
>> collecting the items selected by the client and adding them to a data
>> structure that models a ShoppingCart. In your solution, your client
>> would be be issuing a PUT to the cart URI. You might call this
>> "UpdateCartOperation" but all you've really done is replace the state
> 
> You could also just re-use Hydra's (still existing)
> ReplaceResourceOperation.

No way. If I’m developing an application that has a specific behavior, “ReplaceResourceOperation” is simply too generic to be useful. I literally may not be replacing a thing. Furthermore, replacing the cart will affect other resources. 

> 
> 
>> of cart with no content of what the update entailed. This operation
>> lacks visibility in that one cannot determine if I added a new item to
>> the cart, removed an item from the cart, or simply updated a quantity.
> 
> Right, and exactly that's the point of the uniform interface constraint. You
> don't want three interfaces but just a single one. You want to hide those
> details behind a uniform interface. HTTP isn't concerned about these things.
> Neither are intermediaries. The only ones who are, are the client and the
> server.


Not quite.The uniform interface isn’t about using something so generic that every interaction is reduced to simple CRUD interactions. I’m bring out my dead horse again and demonstrate that HTML certainly doesn’t work this way. 

What I’ve been trying to getting at is than in your CRUD model, only the client knows what they did. You’re now making the server calculate the deltas to derive the action performed. At best, it’s a guess and that derived action may not be 100% correct. That’s the fundamental problem here: REST != CRUD [2] [3] [4] and I can't stress that enough. Bu’t if that’s the assumption that you’re making with Hydra, I’d have to pass on Hydra and use something simple HTML with RDFa instead.  

> 
> 
>> These are 3 distinct activities that affect the same concept. If we
>> called your operation " UpdateCartOperation", developers will have
>> different expectations around the kind of response that gets returned.
> 
> And how is that a problem? They would get the new resource back... or even
> just a 204 No Content.


> 

[1] http://cqrs.wordpress.com/documents/cqrs-introduction/
[2] https://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/10740
[3] http://www.stucharlton.com/blog/archives/2008/06/restful-design-guidelines.html
[4] http://amundsen.com/blog/archives/1063

Received on Tuesday, 4 March 2014 03:53:51 UTC