Aggregates, Services and Entities

time to read 4 min | 602 words

My post about entities and services seem to have hit a nerve, because it is probably my most popular post ever. I got some really good feedback on it. This post is mostly to ask questions, not neccesarily give answers. To make the discussion a bit more focused, here are the scenarios that I am talking about, in the context of a web-based application:

  • Display a list of customer's orders.
  • Calculate total order cost
  • Display all the items in an order
  • Create new order
  • Add new item to order
  • Print full reciept for the order

Quite a few people didn't like the fact that the service is tightly coupled to the implementation of Order.CalculateCost(). Stephen suggested that I would use this type of code:

money = orderService.GetOrder(orderId).CalculateCost()

If that is the case, there isn't any difference between the service and a repository. The problem that I am trying to solve is actually performance, I want to load all the OrderLines assoicated with the order so I could make calculations over them. The code above would either cause lazy load, which is inefficent, or would always load everything, which is also inefficent.

Udi suggested building a set of interfaces for the actions that the class can take, and assoicate fetching strategies with each action, this way we decouple the service from the details of the CalculateCost() implementation. I like the idea, but I am not sure if this isn't too much overhead for the scenario. Decoupling the fetching strategy is good, but I think that it is more appropriate for more complex problems.

Paul suggest going even further with the DDD route. I am pretty sure that I don't understand what he means, so I would like it if someone could educate me. He had this code sample:

public T FindOne(IFetchSpecification fSpec)
{
 fSpec.AddSpan("OrderLines");
 return (T)context.Find(fSpec);
}

Which I don't think is appropriate, once again, I don't always want the order lines, I want them for specific scenarios only. And I am not sure how I can get the above code to behave in both scenarios.

(Currently) the last comment on the previous post belongs to Stephen, he suggests having an abstract type Order, and two decendants: DoesntHaveOrderLines and CalculatedOrder (which does have them). This solve the problem of knowing when to bring the data, but I really don't think that splitting the entities according to performance concerns is a good way to go.