I am building my first web application, it links to the serverside through RESTfull Web API (Angular on client side, ASP.Net Core and EF Core on serverside, Automapper to map API Resources to/from domain models).
In the application I have two main models (simplified below as Delivery
and Order
) and each has its own API controller and endpoint (api/deliveries
and api/orders
). The business rules are such that: 1. an Order
can be created before Delivery
exists, 2. Delivery
can hold an array of Order
that will be included in the delivery, 3. Order
can be amended after being included in Delivery
.
My domain models (API resources are close to identical):
public class Delivery
{
public int Id { get; set; }
public Customer Customer { get; set; }
public Address Address { get; set; }
public DateTime EstimatedDelivery { get; set; }
public DeliveryOrder[] Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public Product Product { get; set; }
public int Quantity { get; set; }
}
public class DeliveryOrder
{
public int DeliveryId { get; set; }
public Delivery Delivery { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
}
The client user interface has a view to create/amend order
(instance of Order
) and a view to create/amend delivery
(instance of Delivery
) which includes the ability to create/amend orders
.
As a result of the business rules, in the delivery
view, I could update order
individually and directly, or update delivery.orders
. I am struggling to come to a suitable implementation.
An example of what the delivery
object looks like on client side, with the first order
being an existing order and the second order
being a new order:
{
"id": 5,
"customer": {
"id": 15,
"name": "Jane"
},
"address": {
"street": "Something Ave",
"number": "145"
},
"estimatedDelivery": "2019-05-21T13:28:06.419",
"orders": [
{
"orderId": 6,
"order": {
"id": 6,
"product": {
"id": 112,
"categoryId": 36
},
"quantity": 4
}
},
{
"order": {
"product": {
"id": 115,
"categoryId": 36
},
"quantity": 6
}
}
]
}
Solutions I have considered:
First. In the view (Angular component) for delivery
have two steps in a "save" action. In this case I need to check if order has an id
or not and either put
an amended order
or post
a new order
to my api/orders
. In this case my Delivery
API (through Automapper profile) ignores Delivery.Orders.Order
and only compares values of Delivery.Orders.OrderId
. My client action would look similar to:
saveAction() {
delivery.orders.forEach(o => {
if (o.order.id) { this.httpClient.put('api/orders/' + o.order.id, o.order).subscribe( ... ); }
else { this.httpClient.post('api/orders/', o.order).subscribe( ... ) });
this.httpClient.put('api/deliveries' + delivery.id, delivery).subscribe( ... );
}
Reading from other topics I understand it is not recommended to create multiple HTTP requests with a for loop to manage the number of subscriptions. Perhaps this is less of an issue if the subscriptions are combined in a forkJoin?
Second. Instead of deciding wether a specific order
existed before the save action, send the whole delivery.orders
to the api and let the serverside decide on whether instances of Order
need to be created or amended. In that case I would patch
the whole array, as discussed here. Again, my Delivery
API (through Automapper profile) ignores Delivery.Orders.Order
and only compares values of Delivery.Orders.OrderId
. The saving action would look something like:
saveAction() {
this.httpClient.patch('api/orders/' delivery.orders).subscribe(data => {
delivery.orders = data;
this.httpClient.put('api/deliveries' + delivery.id, delivery).subscribe( ... )
});
}
Because this would require work on my serverside, I thought maybe it would be better to manage the whole thing on my serverside, which is the third solution I am considering.
Third: Let the server side consume Delivery.Orders
in full and do a custom mapping so that the members in Delivery.Orders.Order
are correctly amended or created. This could be done in either the Delivery
controller or perhaps in a MappingProfile
. The data is then simply sent through:
saveAction() {
this.httpClient.put('api/deliveries' + delivery.id, delivery).subscribe(data => delivery = data);
}
Question(s): I have never done this and I don't feel confident about any of my solutions. Does my third solution make most sense, or should I not have Delivery
API endpoint take responsibility of creating/amending the nested Order
? Is there a design pattern that is considered best practice or at least more commonly used than what I have thought of?