Aggregate Roots vs. Single Responsibility (and other issues)

Tuesday, 27 July 2010 10:34 by ebenroux

It is interesting to note how many questions there are around Aggregate Roots.  Every-so-often someone will post a question on the Domain-Driven Design Group regarding how to structure the Aggregate Roots.  Now, I may be no genius but there are too many questions for my liking.  It indicates that something is hard to understand; and when something is hard to understand it probably highlighting a larger problem.

Aggregate vs. Aggregate Root

"Cluster ENTITIES and VALUE OBJECTS into AGGREGATES and define boundaries around each.  Choose one ENTITY to be the root of the AGGREGATE, and control all access to the objects inside the boundary through the root." (Evans, p. 129)

In one of Eric's presentations he touches on this; stating the difference between an Aggregate and an Aggregate Root.  I have mentioned Eric's bunch of grapes example in a previous post so I will not re-hash it here.

The gang of four has two principles of object-oriented design:

  1. "Program to an interface, not an implementation." (Gamma et. al. p. 18)
  2. "Favor object composition over class inheritance." (Gamma et. al. p. 20)

So what's the point?  At first glance it seems that we are working with composition for both the Aggregate and the Aggregate Root.  On page 128 of Eric's blue book there is a class diagram modelling a car.  The car class is adorned with two stereotypes: <<Entity>> and <<Aggregate Root>>.

Single Responsibility?

So is an Aggregate Root called Car sticking to the Single Resonsibility Principle?  It is responsible for Car behaviour; but also for the consistency within the Aggregate since it now represents an Aggregate with the Car entity as the Root.  It seems as though the Car is doing more than it should and I think that this leads to many issues.

Could it be that the Car Aggregate Root concept is a specialisation of the Car entity?  So, following this reasoning it is actually inheritance.  The reason we do not see it is because it is flattened into the Car entity and, therefore, the car no longer adheres to SRP.  My reasoning could be flawed so I am open to persuasion.

Does the Aggregate Root change depending on the use case?

The problem the Aggregate Root concept is trying to solve is consistency.  It groups objects together to ensure that they are used as a unit.  When one looks at the philosophy behind Data-Context-Interaction (DCI) it appears as though the context is pushed into the root entity.  When a different context (use-case) enters the fray that also makes use of the same Aggregate Root it appears as though the Aggregate Root is changing.

There has been some discussion around the issue of the Aggregate Root changing depending on the use case, i.e. a different entity is regarded as the root depending on the use case.  Now some folks state that the Aggregate Root isn't really changing but the fact that it appears to be changing should be an indication that you are working with different Bounded Context.  Now this is probably true; especially since the word context make an appearance.

A quick note on Bounded Contexts:

Let's stay with the Car example.  Let's say we have a car rental company and we have our own workshop.  Now our car could do something along the lines of Car.ScheduleMaintenance(dependencies) and Car.MakeBooking(dependencies).

This is where issues start creeping into the design.  The maintenance management folks don't give two hoots about rentals and, likewise, the rental folks are not too interested in maintenance; they only want to know whether the car is available for rental.  Enter the Bounded Context (BC).  We have a Maintenance Management BC and Rental Administration BC.  Of course we would probably also need a Fleet Management BC with e.g Car.Commission() and Car.Decommission().

The particular Car is the same car in the real world.  Just look at the registration number.  However, the context within which it is used changes.  It is, in OO terms, a specialization of a Car class to incorporate the context.  This inevitably leads to data duplication of sorts since the data for each BC will probably be stored in different databases.

Assuming we view this as a problem, how could we solve this?  As proposed by the GoF we could try composition.  In DCI terms the context can aggregate the different entities.  I previously blogged about an aneamic use-case model and the context looks an awful lot like a use-case.  I have not played around with how to get these concepts into code but we'll get there.

Repositories return Aggregate Roots?

Now this one is rather weird.  I have no idea where this comes from.  For those that have the Domain-Driven Design book by Eric Evans it is a simple case of opening the front cover and having a look at the diagram printed on the inside where it clearly shows two arrows that point to Repositories.  One comes from Entities and the other from Aggregates (note: not Aggregate Root), like so:

  • [Entities] --- access with --> [Repositories]
  • [Aggregates] --- access with --> [Repositories]

Now if you subscribe to the fallacy that repositories only return aggregate roots then you are really restricting your design.

DDD != AR

Tuesday, 24 November 2009 07:12 by ebenroux

I have been struggling with many aspects of OO development over last couple of years, all in an effort to improve my software.  I have, along with others, been trapped in the DDD Aggregate Root thinking that appears to be everywhere.  It appears as though there is this opinion that ARs are the centre of the universe and I have come to the conclusion that it must have something to do with the consistency boundary afforded by an AR.  This seems to have become the central theme.  Almost as though it became necessary to define DDD in some structural sense.

So, all-in-all, the idea that an the AR changes from use-case to use-case is not true.  The consistency boundary does, however, change.  What contributed to my thinking seems to be the fact that everyone thinks that the only object that may be loaded by a Repository is an AR.  This too is a fallacy.

Any entity may be loaded from a Repository.

So what is an Aggregate Root?

The aggregate concept is not new.  The classic Order->OrderLine example comes to mind.  An OrderLine has no reason to exist without it's Order.    However, people tend to confuse ownership with aggregation.  I know I have.  One may manipulate an Order directly, even though it belongs to a Customer.  One would never manipulate an OrderLine directly.  So an AR boils down to how you manipulate your objects.  The consistency is a side effect since an AR only makes sense as a whole in the same way a class only makes sense as a whole.  Both should remain consistent.

So to manipulate an aggregate we nominate an entity within the aggregate to represent the aggregate.  This becomes the Aggregate Root.

But in many, if not most, cases this entity is only the representative.  Using Eric Evans' example in his 'What I learned...' presentation: an aggregate consisting of a Stem and a collection of Grape objects may have, as the root, the Stem class.  But this does not really represent the GrapeBunch aggregate properly.  In some instances one may probably want a GrapeBunch class and use composition to get to the aggregate.  Mr. Evans mentions that he has no real issue with doing this.  However, I feel too many aggregates end up as abstract concepts in the domain.  It may be that the defined Ubiquitous Language has missed the concept or that the domain experts even missed the aggregation themselves.

Aneamic Use-Case Model

It is my opinion that use-cases (or user stories, or whatever you want to call them) may not receive the necessary attention in our modelling.  Well, mine anyway.  I have been using a 'Tasks' layer but have been trying to move these into my entities and ARs.  This may have been a mistake.

I will be mentioning bits from the use case and sequence diagram Wikipedia articles.

Firstly, we need to see where a use-case fits in.  There are essentially two kinds of 'workflows' in any system: sequential and a state-machine.

Now looking at what a sequence diagram does:

"A sequence diagram shows, as parallel vertical lines ("lifelines"), different processes or objects that live simultaneously, and, as horizontal arrows, the messages exchanged between them, in the order in which they occur. This allows the specification of simple runtime scenarios in a graphical manner."

And what a use-case is:

  • "Each use case focuses on describing how to achieve a goal or task."
  • "Use cases should not be confused with the features of the system under consideration."
  • "A use case defines the interactions between external actors and the system under consideration to accomplish a goal."

A use-case appears to fit the idea of a sequential workflow.  It is completed in one step.  So we can call it an operation.  This operation takes place in response to a command from an actor in the system.  If may then publish an event.  These operations resemble transaction script [PoEAA] and may be why some folks choose to stay away from them since it is confused with an aneamic domain model.  However, they are, in sooth, operation script (also Fowler).  Now, I have been defining a 'Task' for my operations but since moving to DDD-thinking I have been trying to move these into my domain classes.  It isn't working.  I feel that use-cases need to be made explicit also.  They lie in-between task-based domain interaction and state-machines.  The other thing is that one operation may need to interact with another.

State Machines

The term workflow is most often associated with a state machine.  The finite state machine article on Wikipedia may be referenced for more information.

Workflow does not fit into the typical use-case definition and it is probably the reason why the term process is used quite often in business requirements documentation.

Conclusion

What I find interesting is that in any business domain of reasonable complexity one will find all these concepts hidden away.  Developing a computer-based solution that takes all of these factors into consideration is a monumental effort and in many cases is under-estimated.  What I have seen over the years appears to be a tendency to focus on specific technologies to try overcome this intricate mass.  Software such as BizTalk has been abused.

At the root of everything, though, is data.  We need data to represent the real world state.  This leads to a group of developers relying only on data manipulation to perform all these specialized areas.  But everything is built up from the data, so (bold is good):

  • Data Structures
    • Data Manipulation (Procedural Code / Transaction Script)
    • Behaviour (OO Code)
      • Entity-Based Interaction
      • Task-Based Interaction
        • Use-Case Modeling (Operation Script)
          • State Machines (Workflow / Saga)

And to top it all off we will add Service-Orientation.  SOA would wrap all of these.