Shutting down a VM through a REST interface
This is actually a somewhat famous example, put forth by Tim Bray in 2009.
Roy Fielding, discussing the problem, shared this observation:
I personally prefer systems that treat monitored state (like power status) as non-editable.
In short, you have one information resource that returns a current representation of the monitored state; that representation might include a hypermedia link to a form that to request a change to that state, and the form has another link to a resource to handle (each) change request.
Seth Ladd had the key insights in the problem
We've turned Running from a simple state of a person to a true Noun which can be created, updated, and talked about.
Taking this back to rebooting machines. I would argue that you would POST to /vdc/434/cluster/4894/server/4343/reboots Once you've posted, you have a URI which represents this reboot, and you can GET it for status updates. Through the magic of hyperlinking, the representation of the Reboot is linked to the Server that is rebooted.
I think minting URI space is cheap, and URI's are even cheaper. Create a collection of activities, modeled as Nouns, and POST, PUT, and DELETE away!
RESTful programming is Vogon bureaucracy at web scale. How do you do anything RESTful? Invent new paperwork for it, and digitize the paperwork.
In somewhat fancier language, what you are doing is defining the domain application protocol for "shut down a VM", and identifying the resources that you need to expose/implement that protocol
Looking at your own examples
PATCH /api/virtualmachines/42
Content-Type:application/json
{ "state": "shutting down" }
That's OK; you aren't really treating the request itself as its own separate information resource, but you could still manage.
You've missed a little bit in your representation of the change.
With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version.
For example, the JSON Patch media type formats instructions as though you were directly modifying a JSON document
[
{ "op": "replace", "path": "state", "value": "shutting down" }
]
In your alternative, the idea is close, but not obviously correct. PUT
is a complete replacement of the state of the resource at the target URL, so you probably wouldn't choose a spelling that looks like a collection as the target of a representation of a single entity.
POST /api/virtualmachines/42/actions
Is consistent with the fiction that we are appending an action to a queue
PUT /api/virtualmachines/42/latestAction
Is consistent with the fiction that we are making an update to the tail item in the queue; it's a bit weird to do it this way. Principle of least surprise recommends giving each PUT it's own unique identifier, rather than putting them all to one place and modifying multiple resources at the same time.
Note that, in so far as we are discussing the spelling of URI -- REST doesn't care; /cc719e3a-c772-48ee-b0e6-09b4e7abbf8b
is a perfectly cromulent URI as far as REST is concerned. Readability, as with variable names, is a separate concern. Using spellings that are consistent with RFC 3986 will make people a lot happier.
CQRS
What if we have a CQRS domain with many such "actions" (aka commands) that might potentially lead to updates of multiple aggregates or cannot be mapped to CRUD operations on concrete resources and sub-resources?
Greg Young on CQRS
CQRS is a very simple pattern that enables many opportunities for architecture that may otherwise not exist. CQRS is not eventual consistency, it is not eventing, it is not messaging, it is not having separated models for reading and writing, nor is it using event sourcing.
When most people talk about CQRS they are really speaking about applying the CQRS pattern to the object that represents the service boundary of the application.
Given that you are talking about CQRS in the context of HTTP/REST, it seems reasonable to assume you are working in this latter context, so let's go with that.
This one, surprisingly, is even easier than your previous example. The reason for this is simple: commands are messages.
Jim Webber describes HTTP as the application protocol of a 1950s office; work gets done by taking messages and putting them into inboxes. Same idea holds - we get a blank copy of a form, fill it out with the specifics that we know, deliver it. Ta da
Should we try to model as many commands as concrete creates or updates on concrete resources, where ever possible (following the first approach from example I) and use "action endpoints" for the rest?
Yes, insofar as the "concrete resources" are messages, rather than entities in the domain model.
Key idea: your REST API is still an interface; you should be able to change the underlying model without clients needing to change the messages. When you release a new model, you release a new version of your web endpoints that know how to take your domain protocol and apply it to the new model.
Is a CQRS model a better fit for an RPC like API?
Not really -- in particular, web caches are a great example of an "eventually consistent read model". Making each of your views independently addressable, each with their own caching rules, gives you a bunch of scaling for free. There's relatively little appeal to an exclusively RPC approach to reads.
For writes, it's a tricker question: sending all commands to a single handler at a single endpoint, or a single family of endpoints, is certainly easier. REST is really more about how you find communicate where the endpoint is to the client.
Treating a message as its own unique resource has the advantage that you can use PUT, alerting the intermediary components to the fact that the handling of the message is idempotent, so that they can participate in certain cases of error handling, is a nice to have. (Note: that from the point of view of the clients, if the resources have different URI, then they are different resources; the fact that they may all have the same request handler code on the origin server is an implementation detail hidden by the uniform interface).
Fielding (2008)
I should also note that the above is not yet fully RESTful, at least how I use the term. All I have done is described the service interfaces, which is no more than any RPC. In order to make it RESTful, I would need to add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates, and provide code to combine the visualizations in useful ways.