I've been struggling with this issue for a while. Let's say you have a business application that has a rich domain and in one of the front-end views you show a list of open Orders
. In this view you show things like that orderDate, customerName, totalAmount,... but the client also wants to see how many items are in the order (basically the count of OrderItems entity). What is the best way to handle this?
Assumptions
- The
Order
class has aList<OrderItem>
which is fetched lazily - Each ResponseDTO object only contains its core properties and any properties from
@ManyToOne
associations (soCustomer
would be included when fetching anOrder
Solution 1
You can do a rest call for the Order
resource by calling /api/orders
, you'll get a response with the core properties and the customer data. For each order you'll have to do a new fetch to /api/orders/<id>/items
. This is basically an N+1 problem with many HTTP calls.
Solution 2
I change my initial assumption and eagerly fetch collections and immediately include the OrderItems
in the ResponseDTO. The question then becomes, am I going to eagerly fetch each collection of each entity? This seems like a serious performance issue. In this solution the front end would only need to make 1 call.
Solution 3
Initial assumptions are in play, so I lazily fetch collections but I move the N+1 problem from the front-end to the back-end (so no extra HTTP calls), not really a solution but at least the expensive HTTP calls are weeded out.
Solution 4
In my rich domain model I add an attribute to the Order
entity called numberOfItems
. Whenever the method addOrderItem(OrderItem item)
is invoked on an Order
object, I increment the numberOfItems
attribute. It is stored in the database as well. This way I can have all my initial assumptions and use the ResponseDTO with only core properties and I only make 1 HTTP call. The issue I have with this solution is that many people say that you shouldn't store aggregate data like that in the database.
I realize there's no perfect solution and it always depends, but for the life of me I can't seem to find good discussions about this issue. When you google an issue like this you often find information about HATEOAS but that would fit in my Solution 1, which I don't think is really a solution.
If anyone can give an alternative solution or improve on any of the solutions I gave, it would be greatly appreciated.
2 Answers 2
I would agree with @guilaume31 and recommend to use a custom DTO / CQRS Read Model.
With a specified database query you could get the Orders and the number of OrderItems in only one query. Put that result in a specialised DTO and you're done.
Do not use Entities for this query!
Aside from the obvious solution of simply separating your command model from your read model (CQRS) to allow for divergence in terms of structure and fields (which I assume is out of the question), I'd propose either adopting Solution 2 or 4 above.
Although you say 2 is a serious performance issue, is it really? How many OrderItems
are typically included in an Order
? If the number isn't prohibitively large or each OrderItem
isn't particularly complex, I wouldn't expect too much impact, in terms of performance, here. Furthermore, it's pretty common in this domain for Order
to be an Aggregate Root including OrderItems
so that it can enforce all sorts of possible invariants (like maximum price etc.). If that were to ever be the case, you would need to go this route anyway. Additionally, I can also reasonably imagine other instances where a user would like to see the list of OrderItems
in an Order
(that seems pretty fundamental does it not?).
If performance is truly an issue and it is unlikely you will need a list of OrderItems
for some other feature, Solution 4 is your best bet. There is no reason to store numberOfItems
as a field in the database though, and doing so violates the fundamental principals of the Entity Relational Model (there would be more than one way to find that information). The correct approach is to simply modify the query you use for Order
hydration to calculate the numberOfItems
and provide the count in your Result Set. The numberOfItems
property needn't be used during persistence.
Explore related questions
See similar questions with these tags.
NumberOfItems
goes to a new read-sideOrderReadModel
class but not to theOrder
entity.