I love the Actions feature in OData – which is hardly surprising given I was one of its designers. Here’s the main reason why I love it: Actions allow you move from a CRUD architecture style, where you query and modify data using the same model, to an architectural style where you have a clear separation between the model used to query and the model used to update. To me this feels a lot like Greg Young’s baby CQRS, or Command Query Responsibility Segregation.
I’ll admit I’m taking some liberties here because these two models are actually ‘merged’ into a single metadata document ($metadata) that describes them both, and you can share types between these two models… however this feels insignificant because the key benefits remain.
Why would you want to move from a CRUD style application to a CQRS style one?
Let’s look at a simple scenario, imagine you have Products that look like this:
public class Product { public int ID {get;set;} public string Name {get;set;} public Decimal Cost {get;set;} public Decimal Price {get;set;} }
And imagine you want to Discount Marmite (a product in your catalog) by 15%. Today using the CRUD style, the default in OData before Actions, there is only one option: you PUT a new version of the Marmite resource with the new Price to the URL that represents Marmite, i.e. something like this:
POST ~/Products(15) HTTP/1.1 Content-Type: application/json { // abbreviated for readability “ID”: 15, “Name”: “Marmite”, “Cost”: 3.50, “Price”: 4.25 // ($5 – 15%) }
Notice to support this you have to allow PUT for Products. And this has some real issues:
More generally the CRUD model is painful because:
Back to our scenario, it would be much better to disable PUT completely and create a Discount action, and advertise it’s availability in the Marmite resource (to keep your system as Hypermedia driven as possible):
{ “__metadata”: { // abbreviated for simplicity “Actions”: { “#MyActions.Discount”: [{ “title”: “Discount Marmite”, “target”: “Products(15)/Discount”}] } }, “ID”: 15, “Name”: “Marmite”, “Cost”: 3.50, “Price”: 5.00 }
The name of the Action (i.e. #MyActions.Discount) is an ‘anchor’ into the metadata document that can be found at ~/$metadata that says you need to provide a percentage.
POST ~/Products(15)/Discount HTTP/1.1 Content-Type: application/json
{ “percentage”: 15 }
This is much better. Notice this doesn’t allow me to modify the Cost or the Name, and indeed can easily be validated to make sure the percentage is within an acceptable range, and it is semantically much clearer what is happening.
In fact by moving from a CRUD style architecture to one inspired by CQRS but based on actions you can:
Of course simply separating the read/write models in OData doesn’t give you all of these advantages immediately, but at least it creates a foundation that can scale to support things like Event Sourcing or Eventual Consistency later if required.
Some of you may be thinking you can achieve many of these goals by having a more granular model that makes things like “Discount” a resource, and that would be true. However for most people using OData that way of thinking is foreign and more importantly the EDM foundations of OData get in the way a little too. So for me Actions seems like the right approach in OData.
I love this. But what do you think? -Alex