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

On Tuesday, February 18, 2014 10:08 PM, Ryan J. McDonough wrote:
> On Feb 18, 2014, at 10:55 AM, Markus Lanthaler wrote:
> > On Tuesday, February 18, 2014 12:22 AM, Ryan J. McDonough wrote:
> >> In my mind, the equivalent in Hydra would be something like so:
> >>
> >> {
> >>    "@context": "http://www.w3.org/ns/hydra/context.jsonld",
> >>    "@id": "http://example.com/me/cart",
> >>    "operation": {
> >>        "method": "POST",
> >>        "expects": {
> >>            "@type": "AddItemToCartRequest",
> >
> > Yeah, but without that type information.
> 
> 
> Yes.
> 
> >
> >
> >>            "supportedProperty": [
> >>                {
> >>                    "property": {
> >>                        "@id": "productId",
> >>                        "@type": "rdf:Property",
> >>                        "range":
> >> "http://www.w3.org/2001/XMLSchema#integer"
> >>                    },
> >>                    "required": true
> >>                },
> >>                {
> >>                    "property": {
> >>                        "@id": "productVarientId",
> >>                        "@type": "rdf:Property",
> >>                        "range":
> >> "http://www.w3.org/2001/XMLSchema#integer"
> >>                    },
> >>                    "required": true
> >>                },
> >>                {
> >>                    "property": {
> >>                        "@id": "quantity",
> >>                        "@type": "rdf:Property",
> >>                        "range":
> >> "http://www.w3.org/2001/XMLSchema#integer"
> >>                    },
> >>                    "required": true
> >>                }
> >>            ]
> >>        }
> >>    }
> >> }
> >>
> >> I can use @type to be more specific about the intent of this
> >> message. The server would see this message come as a call to
> >> action and there's likely a handler on the server side to 
> >> process an "AddItemToCartRequest."
> >
> > 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>


> 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. 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.



[...]
> >> 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


> 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.


[...]
> > 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)?


> 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.


> 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!?


> 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?


> 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.


> 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.


> 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.


Cheers,
Markus


--
Markus Lanthaler
@markuslanthaler

Received on Friday, 28 February 2014 16:12:01 UTC