Since all entities have a stamped of who created/modified the record, can we consider a Person entity an aggregate root to all entities?
That is, all entities that references the Person will become a collection to Person, e.g.
public class Person
{
public virtual int PersonId { get; set; }
public virtual string Lastname { get; set; }
public virtual IList<OrderHeader> CreatedOrders { get; set; }
public virtual IList<OrderHeader> ModifiedOrders { get; set; }
// Other entities that have a reference on Person will be mapped as a collection under
// the Person entity
}
public class OrderHeader
{
public virtual int OrderId { get; set; }
public virtual DateTime OrderDate { get; set; }
public virtual Customer Customer { get; set; }
public virtual string CommentsOnThisOrder { get; set; }
// stamp audit-level concerns
public virtual Person CreatedBy { get; set; }
public virtual DateTime DateCreated { get; set; }
public virtual Person ModifiedBy { get; set; }
public virtual DateTime DateModified { get; set; }
public virtual IList<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public virtual OrderHeader OrderHeader { get; set; }
public virtual Product Product { get; set; }
public virtual int Quantity { get; set; }
public virtual decimal Price { get; set; }
}
That will basically make all entities become a collection to Person, which violates DDD aggregate root rules.
In my limited understanding of DDD aggregrate, the OrderHeader must not become a collection to Person, as we should not save the Order aggregate via Person. The only *entry* point for saving the Order aggregate(object graph) must be done from the OrderHeader, not from the Person.
Now here comes my real goal even why it looks dirty, I still wanted the Order to be a collection to Person:
There's one ORM *cough* NHibernate *cough* that cannot do a LEFT JOIN (.DefaultIfEmpty) from Person to OrderHeader if OrderHeader is not mapped as a collection to Person. The only way to achieve LEFT JOIN from Person to OrderHeader is to map the OrderHeader as a collection to Person.
Should I allow infrastructure concerns (e.g. facilitating LEFT JOIN from Person to OrderHeader via Linq's .DefaultIfEmpty by making the OrderHeader become a collection to Person) break the rules on when should only an entity must become an aggregate root?
If we will not map the OrderHeader as a collection to Person, then if we needed to make a LEFT JOIN (flattened result, not hierarchical, hence the need to use LEFT JOIN) from Person to OrderHeader on NHibernate, the only option left is to use QueryOver. QueryOver is very tedious to write compared to Linq.
Cannot use the .DefaultIfEmpty(LEFT JOIN functionality) on NHibernate's Linq if we manually do a LEFT JOIN (i.e., via Linq's join and .DefaultIfEmpty) from Person to OrderHeader, .DefaultIfEmpty throws an exception if we do a manual LEFT JOIN on NHibernate, LEFT JOIN on NHibernate's Linq must be done via collection and .DefaultIfEmpty.
If done occasionally(per need-basis), is breaking the rules on aggregate root a pragmatic choice? Example, should I map the OrderHeader as collection to Person in order to facilitate LEFT JOIN from Person to OrderHeader via Linq?
EDIT
Sample northwind database. When we need to report all customers with their orders including those customers without orders(e.g., PARIS)
CustomerID OrderID
OTTIK | 10407
OTTIK | 10684
OTTIK | 10554
PARIS |
PERIC | 10502
PERIC | 10474
PERIC | 10995
PERIC | 10354
PERIC | 11073
PERIC | 10322
, we need to do a LEFT JOIN:
select c.CustomerID, o.OrderID
from customers c
left join orders o on c.CustomerID = o.CustomerID
order by c.CustomerID
That can be done with Linq's join and .DefaultIfEmpty(). However NHibernate's Linq can't do a .DefaultIfEmpty on the result of manual Linq join, it throws an exception. NHibernate's DefaultIfEmpty can only be applied on a collection. But I feel mapping a collection to something that is not an aggregate root violates DDD aggregate root rules. And also doing so, all entities such as Person (Customer is another example) will potentially contain collections of ALL entities, as every table has a CreatedByPerson reference.
@DanielSchilling:
I'm also surprised(in a good way), that referencing the OrderHeader from OrderItem violates DDD. Do we have some sort of whitepaper or a Martin Fowler article expounding on that? I thought referencing a parent entity from a child entity is not considered an infrastructure concern, thus considered DDD.