OOP and REST/JSON Design
public class Person
{
public string FirstName {get;set;}
public string MiddleName {get;set;}
public string LastName {get;set;}
public string FullName {get {return $"{FirstName} {MiddleName} {FullName}";}}
}
When creating a GET endpoint for a resource, should I...
1) Pre-compute method results such as FullName() and send to the client?
2) Re-implement methods such as FullName() client side?
Thoughts:
- Pre-computation would prevent me from having to duplicate the logic client side, and any time the FullName() logic changes, it would only have to be updated in one spot, the server
- FullName() is a pretty trivial example, but I can imagine more complex functions with ever changing rules / validation
- If we pre-compute and serialize function results, it seems like we might run into different problems
- For example, the client does not know about the function dependencies and is left guessing when to re-fetch a person to update FullName()
- A PUT/PATCH request could re-send the representation after modification
- However, this is a trivial example, and doesn't work so well when there are parent/child dependencies, etc
- For example, let's imagine a Shopping Cart and its children Line Items. Cart.TotalPrice() might be a function that sums up the line item prices and their quantities. If the client changes a single line item's quantity, the client would have to be smart enough to re-fetch the entire shopping cart to get the new total. Or..., the PUT/PATCH request could respond with the entire cart and not just the modified line item
- It seems like pre-computing and serializing function results hides object relationships/dependencies from the client, leaving the client "guessing" when state becomes stale
- It seems like re-implementing methods client side is not very DRY and could cause sync issues with ever changing business rules
Would love to hear some opinions / advice on this! 🙂
8 Replies
Ideally, the data you get from the database should be in the exact shape you need
So doing the full name and similar stuff would be a part of your
.Select()
mapping
Or any other form of mapping you'd use
Also, your
can just be
Just a side noteFullName would not be in the database, but a function on the business model
and I'm wondering if the function result should be in the DTO that makes its way to the client
For simple stuff like this, I'd say just don't have it and let the client worry about assembling all 3 names into a full name
Your example with the sum of all cart items, though, I would calculate at the moment of fetching it from the db
And not have it be a function of any model or DTO
Where would that logic be located if not on a class?
Wherever you get your data
EF example:
Ok so you would still pre-compute before sending to the client
Where it makes sense
It doesn't make sense to precompute the full name. The client can do it easily
It makes sense to precompute total items and total cost, on the database, instead of sending a whole-ass list of items
If the client doesn't need that list of items
Oh, I was imagining the client would need all the items and the total
it would be a cart screen
but Imagining the TotalCost function could be pretty complex if there is shipping, taxes, etc
which leaves the question, how does the client know when the DTO becomes stale and they need to re-fetch? Is it just good documentation?
or that modification requests such as PUT/POST/PATCH respond with updated dependencies?
So if you update an item, the response has all resources that were updated as a consequence