views:

152

answers:

1

Most common ORMs implement persistence by reachability, either as the default object graph change tracking mechanism or an optional.

Persistence by reachability means the ORM will check the aggregate roots object graph and determines wether any objects are (also indirectly) reachable that are not stored inside it's identity map (Linq2Sql) or don't have their identity column set (NHibernate).

In NHibernate this corresponds to cascade="save-update", for Linq2Sql it is the only supported mechanism. They do both, however only implement it for the "add" side of things, objects removed from the aggregate roots graph must be marked for deletion explicitly.

In a DDD context one would use a Repository per Aggregate Root. Objects inside an Aggregate Root may only hold references to other Aggregate Roots. Due to persistence by reachability it is possible this other root will be inserted in the database event though it's corresponding repository wasn't invoked at all!

Consider the following two Aggregate Roots: Contract and Order. Request is part of the Contract Aggregate. The object graph looks like Contract->Request->Order. Each time a Contractor makes a request, a corresponding order is created. As this involves two different Aggregate Roots, this operation is encapsulated by a Service.

//Unit Of Work begins
Request r = ...;
Contract c = ContractRepository.FindSingleByKey(1);
Order o = OrderForRequest(r); // creates a new order aggregate
r.Order = o;                  // associates the aggregates

c.Request.Add(r);

ContractRepository.SaveOrUpdate(c);
// OrderAggregate is reachable and will be inserted

Since this Operation happens in a Service, I could still invoke the OrderRepository manually, however I wouldn't be forced to!. Persistence by reachability is a very useful feature inside Aggregate Roots, however I see no way to enforce my Aggregate Boundaries.

Am I overlooking something here? How would you deal with such a scenario?

EDIT: In NHibernate it would indeed be possible to enforce the aggregate root boundary by not marking the aggregate root association with cascade="save-update". I'm stuck with Linq2Sql however.

A: 

Honestly, persistence by reachability isn't really an issue with aggregate root boundaries. Remember, aggregate roots can reference each other just fine. Often, I'll use one aggregate root to create another (Customer.CreateOrder for example). The Order is a root as is Customer, and I can still do Customer.Orders[0].Comments = "Foo".

I'm not in the habit of changing the domain model and not persisting changes, but letting them evaporate. It's just not a realistic use case IMO.

Jimmy Bogard