15 November 2005

It's cool to have a tool

It's the positive "hammer" effect ("when you have a hammer, everything looks like a nail"). We were facing the following problem:

We want to enforce that, when an object is composed of other objects, those objects can not have the same name. This is a strong constraint on our domain model, however it allows us to define "paths" for our entities that are truly unique (a path is composed of entities names from the object to its root parent).

However, what if the user wants to rename an object that is part of a composition? This could break the rule. There are 3 design choices :

  1. have a "rename" method on the composite object doing the check and be careful never to give access to the component objetct (by cloning it when accessed through a getter method for instance)
  2. let the user rename the object directly and then let him validate the composite object (what if he forgets?)
  3. our design choice

I'll explain our choice, of course. Since we have Composition objects that observe their content, it is quite easy to notify them (through an Observer pattern) when the setName() method is called on any of our entity objects. Then, they can do the check (that they usually do when an object is added to them by the way).

This is one of the neat benefits of making the distinction between Compositions and Associations in a domain model (see Eric Evan's book Domain-driven design (DDD)). Many times, when associated objects evolve, the related objects need to be informed to change their state:

  1. when a component changes, the composite must be able to validate that change. A possibility would be to never give access to components in an aggregate (the advice in DDD). This can be pretty restrictive for some domain models.
  2. when an associated object changes (for instance is removed from the database), the related object needs to know about it (otherwise, he may have a dandling pointer). Usually this kind of rules is managed outside of the objects (through a data access layer). But why should this behaviour be separated from the domain behavior?

This is where the hammer hits: by having Composition and Association objects (and variants: Lists, Maps,...) it is really easy to control this behaviour in a very generic way. Each "association container" observes its associated entities and when they face "important" events (renaming, deletion), they notify their container. The container then knows what to do:

  • a OneAssociation has no more associated object: "oops, this must be a critical mistake"
  • a ZeroManyCompositionList is deleted. So should all the components inside the list
  • ...

If I had a hammer,....

No comments: