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

7 comentarios en “Aggregates and repositories

  1. Hi Carlos and thank you for this code…

    I have some questions/comments:

    Do you have any downloadable executable code ?

    How is the following constraint enforced:
    “I should be able to remove and actor (provided it is not associated with any movie)”
    For example, should the implementation of “ActorRepository.Delete(Actor actor)”
    use code like this:
    if(movieRepository.GetByActor(actor).Count == 0) {
    throw new Exception(“Actor can not be deleted if is associated with a movie”);
    }
    In other words, is it okay for one repository to use another, and if not, what is the best alternative ?

    Is the following method really defined according to DDD:
    public interface MovieRepository {
    IList GetByActor(Actor actor);
    The way I understand it, there should be one repository per aggregate (which you indeed do have, i.e. one aggregate/repository combination for Movie and one for Actor).
    However, should not the signature of the query methods only use such attributes that belong to entity/value objects within the aggregate that belongs to the particular repository ?
    In other words, the movie repository is used for returning a movie aggregate, and you defined that aggregate to be a movie instance plus one or more Character instances.
    Indeed you wrote that each such Character instance references one Actor aggregate, but nevertheless that Actor aggregate is belonging to another (its own) aggregate and is not a part of the movie aggregate and therefore the Actor (or any of its attributes) should not (?) be part of any method signature of the movie repository interface ?

    I also miss information about how you would implement the query behind the kind of IMDB page you linked to (Gone with the wind) i.e. how to list all actors/characters for a particular movie.
    One obvious potential interface would be a method something like this:
    public interface ActorRepository {
    IList GetActorsInMovie(String movieName);
    but now again, as my comment above, we would have a situation where the repository interface is using attributes belonging to another aggregate..

    Another solution would be to use the movie repository to retrieve a Movie aggregate, and then use a method “getCharacters()” to retrieve the list of characters.
    As you mentioned, each Character references an Actor, but then the question is when and which class is creating that reference…
    I mean, should the Movie repository use sql that will associate the Character with an Actor (even though the Actor is in another repository) or should lazy loading be used the Character method, i.e. the method “character.getActor()” should be implemented with “return actorRepository.getActorById(this.actorId)” ?
    Well, you did not mention any method “getActorById” but I assume the “primary key method” should almost always be there in a repository interface.
    Another more interesting question is whether the Character class should have an integer field “actorId” (referencing the primary key of an actor).
    You have not specified any database tables here neither, and if you have time and interest it would be interesting with complete downloadable code including database schemas and mappings to classes.

    An entirely different thing:
    What do you actually mean with this method name:
    GetActorsNotAssociatedWithPictures
    Do you not mean
    GetActorsNotAssociatedWithMovies
    ?
    Regarding the ubiquitous language, I think that a movie should always be called a “movie” and not sometimes in method names be refered to as a “picture”, but if there is some actual difference then the distinction should be clarified…

    / Tom

  2. Hi Tom,

    No executable code, I wrote the code in this post :), sorry.

    Regarding deleting actors related to movies, I updated the post with the implementation as I would do it.

    I don’t think that the ActorRepository is responsible for checking that the actor being deleted is not referenced.

    In other words, I see this check more a business logic issue handled in a business logic service.

    I will answer your other questions in another comment.

  3. Regarding the repository question, I think that it’s defined according to DDD concepts, let me explain why.

    Repositories are abstractions for accessing objects of a certain type, these are the aggregates (you may want investigate this in the book or other sources)

    So, I design the repositories according to the aggregate they RETURN from queries (or update/save/delete, of course), no matter the parameters types I use to build the query.

    Please, feel free to elaborate if this explanation is insufficient.

    I’ll answer the remaining question in another comment.

  4. Regarding your question about listing all actors in a movie, I would do it that way:

    Movie movie = moviesRepository.GetById(1);
    Console.WriteLine(“Movie: {0}”, movie.Name);
    foreach (Character ch in movie.Cast)
    Console.WriteLine(“\tActor {0} as {1}”, ch.Actor.Name, ch.Name);

    I think this is similar to your second option.

    Regarding how to retrieve the character or actor instance, I usually use an ORM and this is done with lazy loading, so, I don’t need to reference a repository for this. I don’t have any problem with domain objects referencing repositories, though.

    This sounds good for you?

  5. Okay, regarding the deletion I think it is probably a good idea to use a service as you suggested.
    I did not actually think of that (since I tend to focus, maybe too much, on the OO items and forget to think about procedural services).

    Regarding the lazy loading, I do not understand how an ORM automatically can lazy load an actor from a character, without your domain class (Character) referencing the ORM (or an abstract repository).
    I mean, if your class ‘Character’ is a POCO (without any dependency to infrastructure such as NHibernate) then how can it be lazy loaded without triggering some database query from within your implementation of the method “getActor()” ?
    I would understand if you would have a dependency from within ‘Character’ to your ORM framework or to a repository, but otherwise I do not see how it can be “lazy” loaded, but then rather everything should have to become populated already when the method “moviesRepository.GetById” populated the Movie instance with Characters AND Actors …?

    Finally, regarding the parameters to the query, I am still not convinced that you should allow queries matching values from external aggregates.

    The way I see it, an aggregate defines boundarys for the traversals, i.e. when you navigate and reach the outer limit of the aggregate, then to receive the next object that you conceptually would like to receive a reference from, then you can not keep traversing but will instead have to receieve a referrnce through some other repository.

    Think about like this:
    If you would not use any boundaries in your code, then you might navigate through very remote traversals in code like this:
    a.getB().getC().getD().getE().getF().getG();
    Instead of allowing such traversals, we define som boundaries, e.g. we can choose to say that B and C is part of the aggregate A, so if we would need a reference to D we could let D be another aggregate and define a repository for it.
    So, from my point of view, it would be okay to use attributes from classes B and C, in the query methods of the repository for A, but no more, i.e. we do not allow fields from external structures not part of the aggregate that the repository is defined for.
    Consider a repository using for class “A”:

    public class RepositoryForA {
    public A findBySomeFieldFromClassC(int someFieldFromClassC) {
    // … the returned “a” should match with the following: “a.getB().getC().getSomeFieldOfC() == someFieldFromClassC”
    }
    );

    I think the above would be okay for me, since the query will only use things within the aggregate.
    Though, if I understand you correctly, the following kind of query would be okay for you:

    public class RepositoryForA {
    public A findBySomeFieldFromClassF(int someFieldFromClassF) {
    // … the returned “a” should match with the following: “a.getB().getC().getD().getE().getF()getSomeFieldOfF() == someFieldFromClassF”
    }
    );

    The way I see it, the repository (including the queries) should respect the boundaries of the aggregate and not use concepts from external aggregate structures.
    Do you disagree ?

    / Tom

  6. Hi Tom,

    Believe me, with NHibernate (or Hibernate) and totally POCOs (or POJOs) you can lazy load collections or single references. You may want to check this in the Hibernate documentation.

    Regarding the other comments on the aggregate boundaries, yes, I disagree with you. As I understood the aggregate concept, it defines a boundary for save/modify/delete operations, not for traversal and I don’t see any problem with your “a.getB().getC().getD().getE().getF().getG();” example.

    Many gurus are saying that a good ORM help a lot for a transparent DDD implementation, I think that reading about Hibernate would change your mind about your concerns.

    Regards

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *