David Orchard again tries to defend the Web services stack by analyzing where service-specific state-setting operations are required. His conclusion is unsurprising;
This article has shown that specific write operations are the correct interface design choice for many complex update operations. There are also constrained situations where generic update operations such as HTTP PUT provide superior value. There is no single correct or canonical way to model an interface.
To which I would reply – modulo the choice of the word “many” there – “Of course”. But still, I think the point of generic vs. specific is missed; that generic (PUT in this case) hits an 80/20 sweet spot (give or take). That’s all I’ve ever said, along with the comment that because it’s a sweet spot, it should be the starting point for the Web services architecture, and therefore reified as an architectural style.
That said, there are a few misconceptions in Dave’s post that I’d like to address.
The put timeout operation has an input of the time requested, and an output of the granted timeout.
That would violate PUT semantics. A successful PUT response tells the recipient that the state of the targetted resource was changed to the state reflected in the provided representation. The body of a PUT response can say other things in addition to this, but nothing which violates this contract, as a “granted timeout” would do. Update; actually, there might be a way to do this uniformly, but I don’t think it would provide the expected semantics Dave’s going for since it would require that the timeout was actually set to the provided value at least for the instant in time that determined a successful response could be returned.
Setting a timeout to zero indicates that the resource should be deleted. This is an example of a side-effect: the setting of a value has implications for things other than the value. In this case, it is the very existence of the resource that is affected.
That’s perfectly ok, since it’s an implementation detail, just as it’s an implementation detail that a bank account doesn’t go away if its emptied of funds. Implementation should be separate from interface.
The interface to the PUT operation is different for the timeout and the balance scenario. A client must know the inputs, outputs, and faults for the operation on either timeout or balance resources. The application gains no benefit from re-using the same operation interface. There is also a significant side-effect on one of the operations that is not shared by the other operation. WebDAV uses the term “live” to denote those properties or resources that may have operation side-effects.
No, the interface isn’t different; it’s HTTP, where 404 means not found, 200 means success, etc.. What’s different is the data, not the interface. And again, side-effects are no problem.
The use of a generic operation (which enhances intermediaries visibility into the message) does not bring any benefits because there is nothing re-usable at an intermediary, as shown in the cases of caching and security.
Wait a sec… Dave just (rightly) claimed that the generic operation improves visibility, yet later in the same sentence claims that it doesn’t provide any benefits? Which is it?
Visibility is the benefit that is provided. Perhaps it doesn’t have much value for those two examples (I believe it does, but that’s immaterial), but what about other possible uses of intermediaries that you haven’t forseen? I think it would be very short-sighted to conclude that visibility has no value based on just two examples of intermediaries.
Regarding transactions, I’d say that the scenario David describes there – of batching requests – is not an approach I would take. A general approach (though not by any means universal) that I would take would instead include a mandatory transaction identifier header in each message which was part of the same transaction. Then, in order to perform transaction-level operations, I would perhaps PUT or POST to that transaction resource as necessary. David also adds;
Combining operations that may have side-effects into transactions is potentially dangerous. An operation with a side-effect may cause errors in subsequent operations. An example is a set resource timeout to zero – which deletes the resource – followed by setting a property on the resource. The setting of the property will fail because the resource has been terminated.
Which I don’t think is the case, even if you’re batching, and in the sense of word “side-effect” used above (not in the sense used in HTTP 1.1, which is different). Again, that’s just because implementation should be separate from interface, so a client should not need to care about what happens as a result of an invocation, except as licensed by the interface; HTTP.
So I mostly agree with David’s conclusion, but I think his case could have benefitted from some better examples and a more accurate use and description of HTTP. Had he done that, I expect he would have come to the conclusion – as I have – that while there are certainly some cases where service specific state-setting operations are worth it, PUT, like GET, hits the 80/20 point.
One meta observation to wrap up… Does anybody other than me think that Dave’s interests in finding the happy spot between the Web and Web services might be better served by simply exercising the crap out of PUT (and POST) to test how general its applicability is, rather than starting from the point of view of “All Web services assumptions are valid and here’s why”?