After coming across the same problem, here is the solution I implemented for it.
Domain
public class DomainObject : AggregateRootBase //implements IAggregateRoot
{
private ChildEntity _ChildEntity = null;
public ChildEntity ChildEntity
{
get
{
OnChildEntityRequested();
return _ChildEntity;
}
}
public event EventHandler ChildEntityRequested;
protected void OnChildEntityRequested()
{
if (ChildEntityRequested != null) { ChildEntityRequested(this, new EventArgs()); }
}
public static void SetChildEntity(DomainObject destination, ChildEntity child)
{
//To set or not to set, that is the question.
destination._ChildEntity = child;
}
}
Repository Implementation
public class DomainObjectRepository : List<DomainObjectTracker>, IDomainObjectRepository
{
private void Load() //Method called by constructor or other loading mechanism.
{
foreach (DomainObjectDataModel dodm in ORMSystem) //Iterating through each object returned from ORM.
{
DomainObject do = Translate(dodm); //Translate into domain object.
do.ChildEntityRequested += new EventHandler(DomainObject_ChildEntityRequested);
DomainObjectTracker tracker = new DomainObjectTracker(do, dodm.Key);
base.Add(tracker);
}
}
protected void DomainObject_ChildEntityRequested(object sender, EventArgs e)
{
DomainObject source = sender as DomainObject;
//Here, you could check to see if it is null or stale before loading it.
//if (source.ChildEntity == null || IsStale(source.ChildEntity))
DomainObjectTracker tracker = base[IndexOf(source)];
ChildEntity entity = LoadChildEntity(tracker.Key); //Load the child entity from ORM.
DomainObject.SetChildEntity(source, entity);
}
}
After examining my solution, you may be thinking, "Isn't calling an event infrastructure related code, it certainly doesn't relate to my domain?". While that may be true, if you think about it solely in terms of PI, then you realize that you're just offering a message from your domain saying, "Hey, I'm about to access this child entity." which allows your repository to respond with, "Hold on, if you need one (or a new one), I got it right here in my db so let me give this to you before you continue!".
There are a few caveats here. One, it means that you need an event for every child entity within an aggregate. Two, it breaks encapsulation because you are allowing a public way of setting the child entity. On the flip side, the benefits are expressive code, infrastructure implementations are easily separated from the domain, domain objects are PI, and the ORM remains encapsulated behind the repository implementation.
If someone has a better way of solving this problem, I'm all eyes!
EDIT
Also, even though my answer provides a solution for PI, I agree with the commenters above. You need to evaluate if DDD is even the right answer for you because, while it makes a project easier to maintain and simplifies what would otherwise be a highly complex project, it comes at a cost (usually up-front development time and training your team to organize code properly and utilize the various patterns).