4

I have posted a question regarding Ubiquitous language and maturity level in REST API.

Ubiquitous language and maturity level in REST API?

After some extensive coding I realized that further questions can be derived from this post.

For example, if I am using resource based API, so the API endpoint looks like http://domain.com/products/555 for a basic GET.

In case I want to update a resource it should look like this

http://domain.com/products/555 PUT

expecting a payload such as

{
id: 555,
name: "Sample product",
availability: 111
}

In order to use all of the capabilities of DDD and ubiquitous language, domain model should contain methods such as

  • UpdateProductName
  • ChangeAvailability
  • BlockProductFromFurtherSale etc..

So if API expects this payload, I am thinking of two possible solutions.

a) Split this single update endpoint into multiple such as:

  •  

    http://domain.com/products/555/UpdateProductName PUT
    {
    id: 555,
    name: "Sample product"
    }
    
  •  

    http://domain.com/products/555/ChangeAvailability PUT
    {
    id: 555,
    availability: 111
    }
    
  •  

    http://domain.com/products/555/BlockProductFromFurtherSale PUT
    {
    id: 555
    }
    

b) Have additional logic in application layer in order to

  • compare current domain with new payload
  • detect values that are changed and need to be updated
  • call appropriate domain methods based on changed values

Is there a recommended way to handle this scenaio?

2
  • 2
    Have you considered to use the PATCH verb ? Commented Jun 15, 2016 at 9:35
  • no, but i will definitely take a look at it Commented Jun 15, 2016 at 9:43

2 Answers 2

3

I will try to be exhaustive about the possible solutions that you might use. As you wrote, I consider that a product has the following attributes : id, name, availability

1. Designing a resource for each attributes

  • /products/555/name: GET returns the current name of the product id 555. PUT newname modify the current name of the product id 555 with newname
  • /products/555/availability: GET returns the current availability. PUT 99 modify the current availability to99.

... and so on ...

Notice that the resources are not "method oriented", they correspond to a concept (name, availability, etc. of a product) that could be updated, retrieved, deleted, etc. and not restrained to a particular function.

Notice also that i'm not describing what are the different internally method call when a resource receive a GET or PUT request. That's totally orthogonal. I will write a little note about it, at the end of the answer.

Major drawback of this approach: to update x attributes, a client needs to send x requests... You clearly see that it could be tedious for a client to do that and also bandwidth consuming. But if it's ok, then go ahead.

2. A resource with all of the attributes

  • /products/555 : contains every attributes of a product (name, availability, etc.). A GET returns these attributes. For updating them, you have 2 possibilities:

2.1 updating with PUT

PUT method is intended to fully update the resource representation. It means that your client needs to sends every attributes of a product in the payload.

Imagine that the current representation of resource /products/555 is :

{ "name": "bread", "availability": 10 }

If you want to update just the availability to 99, you need to send the full representation of the resource, like that:

PUT /products/555 { "name": "bread", "availability": 99 }

Major drawback of this approach: A client that want to update one field will need to send the whole representation of the resource.

2.2 updating with PATCH

PATCH verb aims to partially update a resource representation. Considering the current representation of the product is { "name": "bread", "availability": 10 }. If a client want to update only the availability, it will send :

PATCH /products/555 { "availability": 42 }

Be careful, because PATCH is not idempotent. It means that this request:

PATCH /products/555 { "availability": 42 }

can leads to have the following resource representation :

{ "name": "bread", "availability": 42 }

or

{ "name": "sugar", "availability": 42 }

Thus, it breaks idempotency... that PUT guarantee :) ! (since with PUT you send the full representation in the request body)

You could also update several attributes, if your product resource have it. That's obviously less bandwitdh consuming that the solution described in 1.

Note: Underlying methods call

As said previously, methods call are orthogonal to the design of your resources.

In your case, you have some fine grained method to update just one attribute of the Product class... that's ok.

But you could encapsulate this one in a coarse-grained method, something like this:

Product updateProduct(Product p) {
  p.updateName();
  p.changeAvailability();
  ...
  return p;
}
1

Is there a recommended way to handle this scenaio?

You need to review Jim Webber's talk on DDD for Restful systems.

The basic plot - to modify your aggregates, you deliver documents (aka messages) to your HTTP endpoints, and the changes made to your aggregates are a side effect of the document manipulation.

So solution (a) is heading the right direction.

I believe that you should have a separate resource for each command - by which I mean you should be able to distinguish a repeated command from two commands that happen to have the same arguments (on the happy path, it doesn't matter much -- but it can be important if you are sending commands on an unreliable network, like TCP/IP).

In practice, this means that each command has its own, client generated unique identifier, that is used in the URI, much the same way that the product id is used in identifying your product resource.

Since you'll be delivering a complete representation of the command each time, the appropriate method is PUT (or post, if PUT is not supported).

After that, it's just a matter of URI design; subject to the constraint that the URI has to make up for any data you choose to elide from the body of the message itself. But any of the following could be suitable

PUT /commands/a3a14d61-5343-442f-8c2b-febc4d9d8164
PUT /products/555/commands/a3a14d61-5343-442f-8c2b-febc4d9d8164
PUT /products/555/{commandType}/a3a14d61-5343-442f-8c2b-febc4d9d8164
PUT /products/555/commands/{commandType}/a3a14d61-5343-442f-8c2b-febc4d9d8164
PUT /products/555/{commandType}/commands/a3a14d61-5343-442f-8c2b-febc4d9d8164

REST doesn't care what color the bike shed is, so go with whatever fits best with your existing guidelines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.