Archivos mensuales: Noviembre 2008

Aggregates and repositories

This an informal post about this message in the DDD  list.
I really know that this should be answered by a DDD guru but, seeing that no one showed up, I’ll try to fill the space.
UPDATE Nov/19: Added section titles, service implementation to answer Tom’s comment, changed wrong method name from GetActorsNotAssociatedWithPictures to GetActorsNotAssociatedWithMovies (thanks Tom).
Specifications
Let’s say we have this domain: movies and actors acting in that movies.
The goal of this article is to outline how to design this simple domain, to define aggregates (and identify aggregate roots) and the corresponding repositories.
The domain model specifications:
  • Many actors can act in a movie
  • An actor could act in many movies
  • I should be able to remove movies (actors shouldn’t be removed)
  • I should be able to remove and actor (provided it is not associated with any movie)
Let me detail aside the query specifications:
  • I want to retrieve all the movies in what an actor participated
  • I want to retrieve all the movies older that 10 years
Model design
Now let me design the model. We should have at least two objects, Movie and Actor, so let’s start with that classes:
NOTE: code is in C# without IDE assistance and incomplete, for example, trivial accessors omitted.
public class Movie {
private string name;
private int yearPresented;
}
public class Actor {
private string name;
private int yearBorn;
}
OK, pretty simple. Now I’ll try to connect actors and movies. Let’s look for a classic in IMBD, Gone with the wind. If you scroll a page you can see the actors on that movie, they are the Cast, we could build an object Cast but I’ll choose a simple path, more on that later.
Let’s see, for example, for the Scarlett O’Hara character, the actress was Vivien Leigh.
OK, it’s enough of IMDB, let’s go to the model but let me clarify, it’s very important to make the best effort to find the formal (and ubiquitous) language, that’s why I gone to IMDB.
So, seems like a good first step is to build the Character object.
public class Character {
private Actor actor;
private string name;
}
Now is time to define aggregates, I see two:
  • The Actor aggregate including just an Actor instance as aggregate root.
  • The Movie aggregate including a Movie instance as an aggregate root and one or more Character instances. Each Character instance references one Actor aggregate (kind of foreign key in RDMS parlance).
Let me do another round adding some code to my classes (I’m showing code in the  Fowler‘s way):
Mental note: if Martin changed that old picture in his home page I think it’s time to change mine, it’s 10 years old.
public class Movie {

private IList cast = new List();
public Character AddCharacter(Actor actor, string characterName) {
Character newCharacter =
new Character(this, actor, string characterName);
cast.Add(newCharacter):
return newCharacter;
}

}
And the Character class
public class Character {

internal Character(Movie movie, Actor actor, string name) {
this.movie = movie;
this.actor = actor;
this.name = name;
}
private Movie movie;

}
Repositories design
Now I have this domain model pretty much I like it, let me outline the repositories interfaces (one for each aggregate). As you will see, I use the “one method per query” approach, I could use the more concise GetByCriteria approach but it tends to force me to leak the query API to the model.
public interface MovieRepository {
Movie GetByName(string name);
(1) IList GetByActor(Actor actor);
(2) IList GetByCharacterName(string characterName);
(3) IList GetAgedMoreThan(int years);
(4) void Delete(Movie movie)
}
public interface ActorRepository {
Actor GetByName(string name);
(5) IList GetActorsNotAssociatedWithMovies();
(6) void Delete(Actor actor)
}
(1) Should retrieve movies doing a simple join to compare the actor. Pretty easy to do with a good ORM.
(2) and (3) This is a easy queries as well.
(4) Should delete the Movie instances and all the Character instances associated. Actor instances remain untouched.
(5) Not a difficult query, after obtaining the Actors list we could use (6) to delete un referenced actors.
Services design (some of them)
Tom is curious on how to implement the “I should be able to remove and actor (provided it is not associated with any movie)” specification.
I would like to put this into a more real specification like that: “I should be able to purge old pictures and unreferenced actors”. For this spec I would build a maintenance domain service like that:
public interface CatalogMaintenanceService {
public void DeleteDataOlderThan(int yearsOld) {
IList oldMovies = moviesRepository.GetAgedMoreThan(yearsOld);
foreach (Movie movie in oldMovies)
moviesRepository.Delete(movie);

IList orphanedActors =
actorsRepository.GetActorsNotAssociatedWithPictures();
foreach (Actor actor in orphanedActors)
actorsRepository.Delete(actor);
}
}
This service call should be wrapped by a unit of work. The implementation depends on the persistence layer and application platform, for example, in a Web application with NHibernate, it’s usually implemented with a HttpModule.
Please, feel free to ask any question, I would try to answer according my possibilities.
See you