3

Let's suppose I have the following objects in my domain:

class Warehouse
{
    int Id;
    string Address;
    Item[] Inventory;
}

class Item
{
    int Id;
    string Name;
    int Price;
    int WarehouseId;
}

I have a collection of all the warehouses and all the items. I am using a web api to provide access to them in REST style. So, when I retrieve a warehouse, I get something like this:

{
    "Id": 1,
    "Address": "123, 1st street",
    "Inventory":
    [
        { "Id" : "1", "Name": "LED TV", "Price" : "1300", "WarehouseId" : "1" },
        { "Id" : "2", "Name": "Console", "Price" : "500", "WarehouseId" : "1" },
    ]
}

What should be the result of making a PUT request to the warehouse endpoint?

Obviously, it should override the address with the provided one. But should it also override the inventory?

What if it contains a new item, not present in the collection of items? Should it create it?

What if it does not contain one of the items present before? Should it delete it?

  • 1
    Keep It simple. Use PUT to add or replace the whole resource as it has been requested. Use PATCH for deltas. Otherwhise you will end up implementing a sort of diff-comparator. Your business is unlikely to be reinventing the wheel – Laiv Sep 26 '19 at 19:14
  • Sure, but let's say I store these in an RDBMS. To replace warehouse entirely I would have to both create new items and delete old ones, which is not what "PUT" implies. – Heagon Sep 26 '19 at 19:41
  • 2
    Why not? What does PUT implies? Does PUT has nothing to do with your implementation details? As soon as PUT does what semantically is supposed to do is fine. Is it idempotent? Good. Is it unsafe? Good. What else do you expect? – Laiv Sep 26 '19 at 20:25
  • Instead of making Warehouse a complex object, you can make Warehouse to have links to Inventory objects. That way it'll be clear that you update them separately. – imel96 Sep 26 '19 at 23:33
6

I would separate the warehouse from the items:

GET api/warehouse 
Gets a list of warehouses

PUT api/warehouse/{warehouseid} 
Updates some meta information on that warehouse (address etc)

GET api/warehouse/{warehouseid}/inventory 
Gets the inventory of the warehouse (you can supply some search parameters to narrow

GET api/warehouse/warehouseid}/inventory/{itemid} 
Gets a particular item, details, how many in warehouse, etc.

DELETE api/warehouse/warehouseid}/inventory/{itemid} 
Delete a particular item from that inventory or at least decrements the qty that is stored in the warehouse by one.

And so forth.

| improve this answer | |
4

If the Inventory is truly part of your Warehouse resource, then the PUT should override the state of the Inventory array. How you store the data in the back-end, e.g. an RDBMS or NoSQL DB, should not affect the semantics of your REST endpoints. The resources your consumers care about are the ones in your API, not the underlying data structures, so you should make the semantics consistent with the resources you're exposing. However you make that work on the backend, whether a PUT ends up doing inserts in a SQL table or whatever, is irrelevant as long as the PUT semantics are consistent for the resource as defined.

That said, in this particular case, you may be better served by having an API like @JonRaynor suggested, in which the inventory is a sub-resource of the warehouse. This would allow you to separate the updating of the warehouse metadata like the ID and the Address from the transactional data like the inventory, which will probably make life easier for both you (when implementing the back-end data layer), and your consumers (when trying to understand how to use your API).

| improve this answer | |
0

The easiest way to avoid the complexity of updates is to expose endpoints that are use case/command oriented as opposed to CRUD oriented. For example you might have a POST warehouse/{id}/addressChange endpoint that only takes the data related to changing an address as opposed to doing a wholesale update. If you're doing DDD this would be directly reflected in public methods on your warehouse object. The inventory items should only be changed via your warehouse object, if the warehouse is understood to be the aggregate root and it necessary to maintain transactional (as opposed to eventual) consistency between the inventory items and the warehouse.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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