DDD: Cross Aggregate navigation

I've been chewing this one over for a couple of hours now, hitting google, looking for some guidance on how to implement the traversal between one Aggregate to another.

This is what I've found, should anyone care to comment or laugh or just read this damned blog.

Lets draw up an example:

I have 2 entities and a value object: Publishable, User and Document

Now Publishable, and User are both entities, while Document is a VO.

Publishable and Document form an Aggregate (Publishable obviously being the aggregate root as it's the only Entity).

User is essentially it's own aggregate root - lets pretend that maybe it has some VOs or other entities that are subordinate to it).

All well and good so far. Let me explain the relationships between them:

A Publishable has a Document and has an owner association of type User.

As User is NOT part of the Publishable-Document aggregate, and as DDD states, we must only get to non-root entities and VOs via the aggregate root we must navigate from Publishable to User and Document objects. With me so far?

So the question that I've been pondering and that many helpful bloggers out there have already burned many braincells on is "How? How do you cross over from one aggregate to another?"

I forgot to mention that as part of these (2) aggregates, we have 2 corresponding repositories:

PublishableRepository and UserRepository.

So if I have some client code (like a service) working with my Publishable entity:
PublishableRepository pubRepo = new HibernatePublishableRepository(); //ofcourse DI'ed in real life
Publishable pub = pubRepo.get(123);
User u = pub.getOwner();

should my repository have :

1. retrieved Publishable "pub" and Document along with all it's User object (using a hibernate "select Publishable p left join fetch p.owner where p.id = :id" Query for example - assuming Document is declared as a component withing Hibernate)?

2. retrieved the Publishable  and Document and at the same time injected it with a UserRepository  (by the repository directly or via an aspect) so that Publishable#getOwner() delegates to, say, UserRepository#getOwnerForPublishable(PublishableId)

I'm leaning toward number 2 but it still doesn't seem right; injecting domain objects with repositories smells bad. 

There is another option (and I'm sure others will have a number of other suggestions which are welcome as I'm still learning about the nuances of DDD) which is to have the owner association only be an identifier property which can then be used by the client to retrieve the User entity via the UserRepository; therefore not the responsibility of the PublishableRepository.

But this still leaves me with another question. What happens when we are creating a new instance of Publishable?

Publishable p = new Publishable(userRepo.get(2),new Document("readme.txt"));

publishableRepo.add(p);

Well, User is not part of the Publishable-Document aggregate. And we can see that we've retrieved a persistant instance from a UserRepository and passed it to our new instance of Publishable as a constructor parameter. Should the PublishableRepository be concerned with trying to do something with this when add() is called? Should it pass the User object to a reference to the UserRepository? No. This is not it's problem. The User entity already exists - it has an identity as it was retrieved from the UserRepository before being passed into the Publishable constructor.

But what if it's a new entity? What if we are wanting to create a new Publishable with a new User? By "new" I mean that it is not a persistent entity - it has yet to be stored anywhere and has no identity as of yet (it was not got at from any repository).

At this point, it would make sense that the UserRepository#add() method would have to be involved at some point. Perhaps:

UserRepository userRepo = // reference to UserRepository
User u = new User("andy");
userRepo.add(u);

Publishable p = new Publishable(u,new Document("readme.txt"));
PublishableRepository pubRepo = // reference to PublishableRepository
pubRepo.add(p);

That I guess does the trick - but is this the best way? Is it the DDD way?

Should I not be able to do the following?

Publishable p = new Publishable(new User("andy"),new Document("readme.txt"));
PublishableRepository pubRepo = // reference to PublishableRepository
pubRepo.add(p);

and have the PublishableRepository make sure that the User passed in is created if necessary?

Or what if I write the following:

p.getOwner().changeName("moose");
pubRepo.save(p);

Does PublishableRepository call save(p.getOwner()) on a reference to the UserRepository?

This is where my understanding of DDD starts to break down, but I suspect that my thinking is along the right lines. Maybe there is no one-true-way for this, or perhaps I need to re-read Evan's Domain Driven Design once again.

If I find an answer to this I'll be sure to come back and complete this post.

Comments