ILoggable

A place to keep my thoughts on programming

April 25, 2011 geek , ,

HTTP-CQRS: REST+RPC

I started this year with a surprise blogging momentum and it was going really great until i started this post at the beginning of March. I made the mistake of writing a novel on the subject, which just ended up in a meandering draft that completely killed all other writing. Lessons learned: If it takes more than two sessions to write a post, scrap it. So here's a single session redux:

The problem with symmetric data models

REST is wonderful as a query pattern. It easily handles resources and collections of resources and let's you represent hierarchical data models. But for anything than a pure data store, that same pattern is horrible for writes. Posting/putting whole documents at a location comes with complications like what's readonly vs. writable, how are business rules applied, how do you handle partial updates, etc. Yes, it's all possible, but it just imposes lots of ad-hoc and opaque rules on the API.

Before you tell me that you've solved all that, let's just get this clear: Most REST APIs out there either are HTTP-RPC or at least use some RPC in them, but call themselves RESTful, cause it's, like, cool. I'm no REST purist, but I'm willing to bet that your solution to these problems almost always involves a couple of RPC style calls in your RESTful APIs, which only proves my point.

Consider a public user API and how to deal with the user's password:

-- POST:/users --
<user>
  <name>bob</name>
  <email>bob@bar.com</email>
  <password>foo</password>
</user>

-- GET:/users/{id} --
<user id="123">
  <name>bob</name>
  <email>bob@bar.com</email>
  <password-hash>a2e2f5</password-hash>
</user>

-- PUT:/users/{id} --
???

On the POST, we really want the password twice, otherwise we're just a data store pushing the responsiblity for business logic off on the client. On the GET we certainly don't want to return the password. And finally, how do we even update the password? We'd want it in the document twice, plus the old password. So much for a symmetric resource model.

This same problem occurs with Entity models in ORMs: The query and write data models are treated as symmetric, when in reality what we query for and what we manipulate seldomly follows the same model. Query models usually end up getting simplified (flattened, normalized) to favor updates, and update models contain mutable data inappropriate for a specific action.

Separating queries and commands

On the data manipulation side, the CQRS (Command-query Resposibility Separation) pattern has been gaining favor. In it, data is retrieved via queries that match view models, while commands take only the data affected by the command and the command explicitly reflects the user story it advertises.

Commands are procedural, taking as input only the data they require. That certaintly matches HTTP-RPC: It's not a modified resource being stored, although the contract may imply the manipulation of a resource. This pattern gives far greater freedom to manipulate subsets and supersets of resources than a REST PUT can offer and is a more natural match for how data is manipulated in user stories.

On the query side, we've freed REST from representing models that need to be modifiable via PUT, allowing more complex and denormalized data. Yes, this breaks the REST mantra of canonical location of a resource, but that mantra is largely a reflection of having to have a canonical location for manipulating the data. Once models are query only, denormalization isn't a problem anymore, since the command responsible for modification takes on the responsibility of making sure the denormalized changes are appropriately propagated.

Together the use of HTTP-RPC for write and REST for query, we get HTTP-CQRS. Applying this pattern to that public user API from before, we might deal with the password like this:

-- POST:/commands/users/create --
<user>
  <name>bob</name>
  <email>bob@bar.com</email>
  <password1>foo</password1>
  <password2>foo</password2>
</user>

-- GET:/query/users/{id} --
<user id="123">
  <name>bob</name>
  <email>bob@bar.com</email>
  <password-hash>a2e2f5</password-hash>
</user>

-- POST:/commands/users/{id}/changepassword --
<command>
  <old-password>foo</old-password>
  <new-password1>bar</new-password1>
  <new-password2>bar</new-password2>
</command>

While you could go all SOAP-like and just have a /commands endpoint and require the action in the body, using descriptive URIs greatly simplifies API comprehension, imho. By separating query and command reponsibility for web services the API actually becomes more descriptive and opens up a lot of operational patterns that aren feasible or at least not sensible with pure RESTful APIs.

5 to “HTTP-CQRS: REST+RPC”

Trackbacks/Pingbacks

  1. […] complexity and therefore have not passed the theory level. Anyway, after reading the article “HTTP-CQRS: REST + RPC” I have decided to write this, because I think they respond to the same […]

  1. Bjorg says...

    I don't know if I fully agree.  For the query side, there are multiple representations that exist depending on the visualization.  Maybe that would be done using an Accept header or a 'view' query parameter on the GET request. The latter is more browser-as-cli friendly and that gives it the edge, imo.  For the command side, just doing a POST against /users/{id} with the body distinguishing the command is preferable to lots of end-points (a bit like SOAP… gasp!). It would also make queuing multiple commands trivial (update name, change password, etc.) with a single POST.  If commands are queued in an async manner, the design will still need an end-point to query the status, but that could simply be GET:/commands/{id} (where this URI is returned in a 202 response header).
    In the grand scheme, those are details though.  I do believe REST as machine-to-machine pattern has mostly failed and we should move onto something else.
    PS: there's a typo, 'Resposibility' should read 'Responsibility'.
     
     

     
     

  2. Bjorg says...

    After thinking about this for a bit longer, I don't like my proposed POST anymore. The issue is that it strongly binds the implementation to a body type that can represent the command.  That makes it impossible to use simpler bodies like CSVs without wrapping a document arount them. So, I'm leaning towards POST:users/{id}/{command}. Alternatively, there could be a ?command= query parameter.  Either way, the command distinction is removed from the request body.
    Also, there should be a way to query for all available views and commands.  Something like, GET:users/views and GET:users/commands. That's assuming, {id} can't be 'views' or 'commands' of course.

  3. Daniel Calbet says...

    Reading your article has made me write another with some old ideas about “original data” services versus “functionality services” and Rest extensibility. It shows another approach to the same problem.
    You can read it here:
    http://blog.notifychanged.com/2011/04/26/an-extesible-rest-api/

  4. arne says...

    Bjorg,

    I like the idea of having a way to discover views and commands. Discoverability was always one thing that REST as a pattern lacked.

    Can't decide whether i prefer {verb}:{type}/{id}/(views|commands)/{…} or {verb}:(views|commands)/{type}/{type}/{…} better. I could make an argument for either.

  5. Notify Changed » Blog Archive » An Extensible Rest API says...

    […] complexity and therefore have not passed the theory level. Anyway, after reading the article “HTTP-CQRS: REST + RPC” I have decided to write this, because I think they respond to the same […]

Leave a comment