views:

418

answers:

5

Hopefully, this fictitious example will illustrate my problem:

Suppose you are writing a system which tracks complaints for a software product, as well as many other attributes about the product. In this case the SoftwareProduct is our aggregate root and Complaints are entities that only can exist as a child of the product. In other words, if the software product is removed from the system, so shall the complaints.

In the system, there is a dashboard like web page which displays many different aspects of a single SoftwareProduct. One section in the dashboard, displays a list of Complaints in a grid like fashion, showing only some very high level information for each complaint. When an admin type user chooses one of these complaints, they are directed to an edit screen which allows them to edit the detail of a single Complaint.

The question is: what is the best way for the edit screen to retrieve the single Complaint, so that it can be displayed for editing purposes? Keep in mind we have already established the SoftwareProduct as an aggregate root, therefore direct access to a Complaint should not be allowed. Also, the system is using NHibernate, so eager loading is an option, but my understanding is that even if a single Complaint is eager loaded via the SoftwareProduct, as soon as the Complaints collection is accessed the rest of the collection is loaded. So, how do you get the single Complaint through the SoftwareProduct without incurring the overhead of loading the entire Complaints collection?

A: 

I am using NH for another business context but similar entity relationships like yours. I do not understand why do you say:

Keep in mind we have already established the SoftwareProduct as an aggregate root, therefore direct access to a Complaint should not be allowed

I have this in mine, Article and Publisher entities, if Publisher cease to exist, so do all the dependent Artcle entities. I allow myself to have direct access to the Article collections of each Publisher and individual entities. In the DB/Mapping of the Article class, Publisher is one of the members and cannot accept Null.

Care to elaborate the difference between yours and mine?

Sorry this is not a direct answer but too long to be added as a comment.

o.k.w
I am definitely not a DDD expert, but my understanding is that one of the main concepts of aggregate root is that the child entities should only be manipulated through the root. I could be misunderstanding something, but I have seen this rule stated many places. This link discusses the concept of aggregate roots as well. http://dddstepbystep.com/wikis/ddd/blogged-aggregates-and-aggregate-roots.aspxI think are situations are the same, so you would use an Article Repository to retrieve the specific article, which then would have a Publisher as well?
@pbrophy: Yes, I do retrieve a specific article in my app. In your case, if SoftwareProduct is a HUGE entity with many other memebrs, you can add specific projections/transformers so that you do not load the entire entity, just those that you need.
o.k.w
So, when you return the article, do you edit items via the article or the publisher. In other words, do you say Publisher.Articles[0].Title = "New Title" PublisherRepository.Save(Publisher) ORArticle.Title = "New Title"ArticleRepository.Save(Article)
@pbrophy: Yes I use `Article.Title = "New Title"` and then `ArticleDAL.Update(Article)`
o.k.w
+3  A: 

This gets a bit into semantics and religiosity, but within the context of editing a complaint, the complaint is the root object. When you are editing a complaint, the parent object (software product) is unimportant. It is obviously an entity with a unique identity. Therefore you would would have a service/repository devoted to saving the updated complaint, etc.

Also, i think you're being a bit too negative. Complaints? How about "Comments"? Or "ConstructiveCriticisms"?

Josh
A: 

@Josh,

I don't agree with what you are saying even though I have noticed some people design their "Web" applications this way just for the sake of performance, and not based on the domain model itself.

I'm not a DDD expert either, but I'm sure you have read the traditional Order and OrderItem example. All DDD books say OrderItem belongs to the Order aggregate with Order being the aggregate root.

Based on what you are saying, OrderItem doesn't belong to Order aggregate anymore since the user may want to directly edit an OrderItem with Order being unimportant (just like editing a Complaing with its parents Software Product being unimportant). And you know if this approach is followed, none of the Order invariants could be enforced, which are extremely important when it comes to e-commerce systems.

Anyone has any better approaches?

Mosh

Mosh
A: 

To answer your question:

Aggregates are used for the purpose of consistency. For example, if adding/modifying/deleting a child object from a parent (aggregate root) causes an invariant to break, then you need an aggregate there.

However, in your problem, I believe SoftwareProduct and Compliant belong to two separate aggregates, each being the root of their own aggregates. You don't need to load the SoftwareProject and all N Complaints assigned to it, just to add a new Complaint. To me, it doesn't seem that you have any business rules to be evaluated when adding a new Complaint.

So, in summary, create 2 different Repositories: SoftwareProductRepository and ComplaintRepository.

Also, when you delete a SoftwareProduct, you can use database relationships to cascade deletes and remove the associated Complaints. This should be done in your database for the purpose of data integrity. You don't need to control that in your Domain Model, unless you had other invariants apart from deleting linked objects.

Hope this helps,

Mosh

Mosh
A: 

I agree with Josh and disagree with Mosh.

The relation Order-Orderitem is not the same as the relation Product-Complaint. In the database both can be modelled as foreign keys with cascading deletes, but that by itself does not automatically mean it should be modelled as Order-OrderItem. I ask Mosh: How would you model the relation Customer-Order?

Following Mosh's line of thought I think you would have only Customer as an aggregate root and then each Customer would have a list of Orders. But IMHO that would be a mistake. The relation Customer-Order is just a relation. An order has a reference to a Customer, but Customers do NOT have a list of Orders. You can Query the Order repository to get the Orders for a Customer, but you might also query Orders by period without specifying a Customer.

The Orders of a Customer in general is not a special logical unit where the user adds and removes Orders. Instead you just create an Order (from the main menu) and then pick a Customer, because an Order requires a reference to a Customer.

Dave