It's been five years since Eric Evans' classic book Domain-driven design: tackling complexity in the heart of software was published. Last week two interesting articles came out; What I've learned about DDD since the book by Eric himself and Domain Models: Employing the Domain Model Pattern by Udi Dahan. They both talk of lessons learnt about some of the concepts of domain-driven design (DDD).
Udi talks about a very common misconception about domain Entity objects, what I like to call the Fat Domain Model: using the persistent object model as the Domain Model. I've done this myself for many years, trying to avoid Martin Fowler's Anemic Domain Model and Fat Service Layer anti-patterns by adding the domain entity's behavior to the related data entity object.
Microsoft does a variant of the Fat Service Layer in their Three-Layered Services Application guidance: they use anemic Business Entity objects and keeps the business logic in separate Business Component objects, exposed by Service Interface objects. So coming from this, it seemed natural to merge the entity and logic into one when I started practicing DDD.
The description of the Entity pattern from Eric's book is clear on what goes into the entity object:
"It is natural to think about the attributes when modeling an object, and it is quite important to think about its behavior. [...] strip the Entity object's definition down to the most intrinsic characteristics [...]. Add only behavior that is essential to the [domain] concept and attributes that are required by that behavior."
So, only the data that is required by the domain logic should be in the domain object. Eric use the term "information about the business situation" in the book when explaining the Domain Layer, another hint that this is not traditional data entities. Querying and viewing entities is not domain logic, so there is no need to add all attributes of the ORM data entities used in the presentation layer to the DDD Entity object. Querying and viewing entities belongs to the Service Layer. I suggest using two separate object models (Command and Query Responsibility Segregation, CQRS) as shown in Udi's article, but think twice before implementing your forms-over-data application using the Domain Model pattern.
In his QCon 2009 presentation, Eric talks about the DDD Aggregate pattern and how it can be hard to model an Aggregate Boundary and decide to which Entity object a specific behavior belongs. Here is the definition of Aggregate from the book:
"First we need an abstraction for encapsulating references within the [domain] model. An Aggregate is a cluster of associated [domain] objects that we treat as a unit for the purpose of [ensuring consistency of] data changes. Each Aggregate has a root and a boundary. The root is a single, specific Entity contained in the Aggregate."
I think the Aggregate pattern is easy to misunderstand as it focuses on modeling the relationships and boundary of the root and its associated collection of domain objects - immediately making people think of the classical "purchase order with items collection" OO data encapsulation design or even ER-design. In addition, the strong focus the Aggregate being an abstract consistency mechanism leads to the misconception that Aggregate domain logic must belong to the root Entity object and that logic that involves associated Aggregate data will require that data to be modeled into the collection of domain objects.
Eric uses the example of calculating the weight of a grape cluster Aggregate. This logic doesn't belong on the grape Entity, so the only place to put this is on the stem Entity - because the Aggregate itself is not an object, just an abstract concept. He also talks about how this kind of logic typically is designed as an iteration of the associated object collection, while lessons learnt has shown that this OO legacy mindset is not mandated by the domain business rules. Udi also touches the same problem in the "UnpaidOrdersAmount" example in his domain model article.
The fact is that the domain aggregate is not just an abstract concept, it should be modeled as what I like to call a Concrete Aggregate. This alleviates the root Entity from getting responsibilities that doesn't naturally belong to it. This kind of non-entity domain logic goes into the Concrete Aggregate as its behavior and prevents leaking domain logic into the Service Layer.
Using Concrete Aggregate objects might also lessen the need for fully modeling objects within Aggregate boundaries. E.g. having a GrapeCluster object that can calculate its weight directly without loading a Stem and its collection of Grape objects makes the domain model much simpler. Again note the focus on business rules behavior rather than OO entity-relationship modeling.
I admit struggling with naming the Concrete Aggregate pattern, maybe Aggregate Entity would be a more self-explanatory name.
No comments:
Post a Comment