Anthony Walters
Anthony Walters

Reputation: 143

DDD Aggregate Root Persistence

I have a class which is an aggregate root and represents a Person. A person must have a Title ( Mr, Mrs, Ms etc ) which is a property of the Person object. When creating a person the user must select a title from a drop down list the contents of which are administered via another page.

There is also the option to view this information via a read-only page so by using the model below I have the Id and Name "to hand" and do not have to go away and retrieve the ValueName of the title based on the title id which is what I would have to do if I were to add TitleId as a property of Person rather than Title.

When saving a person object the Id of the title is persisted as part of the person and is stored in the Person db table (the db table containing the titles is not touched)

When populating person the stored procedure joins person on to title based on title id and returns information used to populate the person an title classes.

public class Person : IAggregateRoot, IPerson 
{
   public string Forename { get;set; }
   public string Surname{ get;set; }
   public ITitle Title { get;set; }
}

public class Title : IAggregateRoot , ITitle
{
    public Guid Id {get;set;}
    public string ValueName {get;set;}
}

My question is: From a DDD perspective is it ok to use this class structure and have an aggregate root object nested within another aggregate root object given that it is a "fixed list" or "lookup value" and also needs to be maintained separately by an administrator?

Upvotes: 1

Views: 1272

Answers (2)

Aaron Hawkins
Aaron Hawkins

Reputation: 2691

Aggregate roots are the "ROOT" of the aggregate by definition so the short answer to your question is no, aggregate roots cannot be nested within another aggregate root.

Keep in mind that the aggregate root is all depending upon context. Therefore, in the context of managing a "Person", Title is just an entity or value object. In the context of managing a "Title", Title may be the aggregate root. If this is the case, there should be two separate "Title" classes, one for each context. This is a change management strategy so that changes made in one context do not affect the other context.

Here is an example that might help shed some light on the subject. Please forgive the names as more knowledge of your domain would be required to make more meaningful names so I'm going to make some assumptions for the purpose of this example:

Context of Hiring a Person

public class Person : IAggregateRoot, IPerson 
{
   public string Forename { get;set; }
   public string Surname{ get;set; }
   public Title Title { get;set; }
   public DateTime? HireDate { get; set; }
   public void Hire(Title granted, IPersonRepository repository)
   {
       Title = granted;  //Grant the new hire this title that we have at our company
       HireDate = DateTime.Now;
       repository.Save(this);
   }
}

public struct Title /* I like to make my value objects structs */
{
    public Title (Guid id, string value)
    {
        Id = id;
        Value = value;
    }
    public Guid Id { get; private set; }
    public string FullTitle{ get; private set; }  /* This would be the prefix + value + level when loaded from the repository because, in this context, we don't have any need for that level of separation. */
}

Context of creating a new title with different levels

public class Title : IAggregateRoot, ITitle
{
    public string Prefix { get; set; }
    public string Value { get; set; }
    public int Level { get; private set; }
    public void Create(int levelsAvailable, ITitleRepository repository)
    {
        for (int i = 1; i <= levelsAvailable; i++)
        {
            Title title = new Title(Prefix, Value) { Level = i };
            repository.Save(title);
        }
    }
}

public class Person : IEntityObject, IPerson 
{
   public ITitle Title { get;set; }
   public string Name { get; set; }
}

To illuminate the reason for the change management strategy, consider if the business comes back and says that every new hire is to be granted a probationary title for a period of 90 days and that probationary titles are defined as level 1 titles. Using this strategy above, only one context would need to be changed which eliminates any possible unknown failures for the other context.

Hope this helps!

Upvotes: 1

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241485

No, it's not okay to nest aggregate roots. An aggregate can contain multiple entities, or references (by id) to another aggregate, but it cannot encapsulate an aggregate.

I would also say that "Title" is not a good candidate for an entity (aggregate or otherwise). It should be considered a "value object", and really - it should just be a string. You may have a separate lookup table of titles, but that doesn't mean you should reference that table from your Person object.

Separate the process of maintaining the titles lookup table from the assignment of title to a person. When a title is chosen, copy the string to the person object. Denormalization is the way to go with value objects.

Upvotes: 0

Related Questions