Home > OS >  How to design REST API for aggregate with nested objects?
How to design REST API for aggregate with nested objects?

Time:12-06

I'm designing my first REST-API and am struggling to design the API for a Cart aggregate that has nested objects:

Cart 1->n SubCart 1->n CartItem.

The Cart aggregate is saved as a whole. Each time an item is placed in the Cart various discounts have to be calculated that depend on the other CartItems even from other SubCarts. Therefore the client app needs to receive a full Cart aggregate with all nested objects.

Evens says in his DDD book that "... The root is the only member of the AGGREGATE that outside objects are allowed to hold references to...".

I understand the general idea, but it is not clear what reference means. Some articles speak about in memory object references, others speak about id references, too. If the user i.e. wants to increase the quantity of an CartItem the UI must have a way to identify the item in the cart. It needs to have some id/reference. Otherwise how shall the Cart know what item's quantity should be increased. What did Evans mean with reference? The way it is worded is very confusing.

Since all commands go through the Cart aggregate in the backend I wonder if this also should be applied to the REST API. Since I lack the experience I don't know what issues or side effects one or the other solution will have (i.e. caching, security)? Why would one prefer one or the other?

For example for updating the quantity of a CartItem I see the follwing options:

  1. PATCH carts/{id}/subcarts/{subcart_id}/cartitems/{cartitem_id}

  2. PATCH carts/{id}/{subcart_id}/{cartitem_id}

  3. PATCH /carts/{id}?subcart={subcart_id}&cartitem={cartitem_id}

  4. PATCH carts/{id} having subcart_id and cartitem_id in the body

I saw that GIT API used the shorter form of option 2 in some cases. When should one chose option 1 over option 2?

For options 3 and 4 it would be natural to return the new Cart object with possible discounts as a result of the PATCH.

With option 1 and 2 it seems not RESTful to return a Cart object after a PATCH of a CartItem. The return could be a 204 and the client must then send a GET on the Cart again, resulting in two calls.

Thanks for any help and insights.

CodePudding user response:

My suggestion is to keep it as simple and understandable as you can.

First of all, REST guidelines are conventions, not rules written in stone. Also, you say you are afraid not to be RESTful. I'm almost sure you will not be so anyway :-). For example, I'm almost sure you are not going to implement HATEOAS at all, and without it you are not creating a RESTful system (like the vast majority of the so-called REST APIs you find around, after all :-))

That said, you should think in terms of resources and CRUD operations you want to perform on them. Since discounts depend on the presence of other items in subcarts, my suggestion is to consider the cart as your resource, including its subcarts and items. This simplifies your work and the overall understanding of the system.

Make your decision based on performance and clarity issues.

CodePudding user response:

Even the idea behind it is clear to me, it is not clear what reference means. Some articles speak about in memory object references, others also speak about id references. If the user i.e. wants to increase the quantity of an item the UI must have an id/reference to that item. What did Evans mean with reference?

Pointer.

class A {
   B b
}

In this example, A "holds a reference to B". According to Evan's guidelines, if A and B are both domain entities, then either this instance of B is a member of the same AGGREGATE as this instance of A or B is itself the root entity of its AGGREGATE.


How to design REST API for aggregate with nested objects?

A REST API is a collection of resources, where resources can be understood to be a generalization of "web pages". Clients send messages to manipulate resources, and useful business activity is a side effect of manipulate the resources. See Webber, 2011.

In other words, the client sends a PATCH/POST/PUT message to the HTTP server, and the server in turn invokes some command(s) on the corresponding aggregate root entity.

PATCH /carts/{id}/subcarts/{subcart_id}/cartitems/{cartitem_id}
PATCH /carts/{id}/{subcart_id}/{cartitem_id}
PATCH /carts/{id}?subcart={subcart_id}&cartitem={cartitem_id}
PATCH /carts/{id}

All of these are fine, in the sense that you can have as many different resources as you like that change the same aggregate root, and also in the sense that you can use any spelling conventions you like for your resource identifiers. The only real constraint here is that the identifiers should be RFC 3986 compliant.

Note: keeping all of the client's locally cached copies of different resources synchronized after changes can be tricky; so if you aren't sure you know what you are doing yet, then my suggestion would be to have a single resource per aggregate.

It is okay to use POST, and you will probably find that sending representations of domain model commands to the server with POST makes the implementation easier than trying to compute a command from a patch document. Remember, the web was catastrophically successful using HTML forms and POST.

  • Related