From Evans, the rules we need to enforce include:
- The root Entity has global identity and is responsible for checking invariants
- Root Entities have global identity. Entities inside the boundary have local identity, unique only within the Aggregate.
- Nothing outside the Aggregate boundary can hold a reference to anything inside, except to the root Entity.
- Objects within the Aggregate can hold references to other Aggregate roots.
- A delete operation must remove everything within the Aggregate boundary all at once
- When a change to any object within the Aggregate boundary is committed, all invariants of the whole Aggregate must be satisfied.
Don't understand me wrong, I really would love to apply this pattern. The essence of aggregates to identify a cluster of objects that can be conceptually thought of as one unit is very important for managing complexity. But is it practical in our today's programming model? On the domain driven design discussion board I found an interesting discussion about aggregate boundaries between DDD adopters and Eric Evans. From the discussion I see that we're not the only ones that have problems with the implementation of aggregates. And what's interesting is that in the same discussion Mr. Evans wrote that he'll try to add a concrete example and that he we'll come up with some useful new insight - he didn't return to the discussion.
Does anybody has a real world example of aggregates? One that enforces all the rules?
2 comments:
I wanted to reflect more about the code-metrics of our project than about the aggregates itself in my last post. However I would like to give you an example of what I like and what I don’t like in how we implement the aggregate pattern:
What I like for example: repositories and aggregates build together a single concept. It’s clear that when I need an aggregate (e.g. person) I get it from the repository (e.g. person-repository). Query-logic is pretty good encapsulated in the repositories and we have found a good place to do some more advanced mapping (e.g. enrich the user-object with information from the active directory).
I don’t like that we are too tight coupled to the database model. We don’t take advantage of more advanced mappings; therefore our database model is always present. Why I need to instantiate a new address-object and than add it to address-collection of the person? Why I can’t just write person.SetNewAddress(“Cathedrale Square”, new DateTime(2009,1,1))? In my trial, which I mentioned in my post, I found out that I had less code at the end.
I was just watching at the ‘Muenzliste’-implementation that I liked very much because things like that is what we need. But we could go ahead and separate the database-model from the domain-model and make out of the ‘Muenzliste’ two classes. One represents the database-model the other class is a value-object returned by the aggregate root. We still would access it via get;set on the aggregate root, but the design would clearly indicate what functionality is important on the presentation layer.
The discussion about DDD shows in my opinion that there is no general rule :-(. There is always more than one solution to solve a problem, but I think the design should clearly state what solution was chosen. I think the aggregate pattern is at least a valuable guidline that helps us to structure our domain-logic.
Yes, you're right, it's a valuable guideline. I would even say it's not a design pattern, as it lacks of implementations
Post a Comment