DDD: Aggregate and entities relationship

Viewed 534

I read some of the publications and threads about DDD. There are many voices about connection between aggregate and entities. I understood that aggregate should be as simple as possible (one entity per aggregate). But what about situation when aggregate has collection of entities?

Let's say, we have one aggregate called "Month" which contains a collection of "Day" objects (that are domain entities, because they need an identity to be distinguishable - to let aggregate know which "Day" to modify).

So I have two questions:

  1. Is this a proper approach? Just a normal situation and I shouldn't be concerned?
  2. What about "visibility" outside? In my approach, an aggregate is "package-private" to not let anyone use it in different parts of the system. But what about entities? Should they be visible just like Value Objects for different parts of the system? Or just create another VO to represent entities outside (for example: when entities are stored in events)?

Thank you for all the answers

2 Answers

Modeling days and months as entities depends a lot on the context. It might not be the best example to explain aggregates and entities but let's give it a try.

Let's assume that in our context a day can not exist on its own. It has to be within a month. If you want to refer to a day, you must specify the month first. That is how we use dates in real life, January 1st, May 8th ... Even though the days are entities they don't need a global uniques identifier. They only need an identifier within the month [1 .. 31].

Aggregates should be as small as possible however it is not a rule that you should only have one entity per aggregate. You just need to have an aggregate root (month) that has a unique identifier across all the systems. Within the aggregate, you can have entities(days) that have a unique identifier within the aggregate[1 .. 31]. If you want to refer to or access these entities you should always go through the aggregate root.

Aggregate, Entity and ValueObject

In my vision, an entity is a child with a name (Id) of an Aggregate which, in turn, is the Father with a name (Id).

An aggregate can have NO entity. You can think well is an entity, NO: an entity exist ONLY within an Aggregate.

The entity (child) is a small aggregate (father) without other entities (children's).

Fathers and children can use transparent box (I don't have a better idea to translate the concept of ValueObject, sorry): when you create a transparent box you cannot change anything but you can read the content, if you want to update the box you MUST create a new one.

The aggregate is responsible for manage the entities, this means: add, update, remove and query.

If you want to talk (query) with an entity or entities you must ask the aggregate, consequentially, when you load the aggregate you MUST load all entities.

An aggregate can have multiple types of entities, how many? Well, this depends on you, on the design, and on the system.

Obviously, a big aggregate of many fields with many entities with each of them many rows maybe is not efficient, in this case, maybe you can choose the biggest entities and turn in Aggregates with or without children.

Pratical example

I wrote the example on csharp but is not much different from Java.


class Invoice : ValueObject
{
    public string Number { get; private set; }

    public DateTime Date { get; private set; }

    public decimal TaxableAmount { get; private set; }

    public decimal VatAmount { get; private set; }

    public decimal TotalAmount => TaxableAmount + VatAmount;

    public Invoice(string number, DateTime date, decimal taxableAmount, decimal 
         vatAmount)
    {
         // validation
         Number = number;
         [..]
    }

}

class Taxonomy : Entity 
{
   public int Id {get; private set;}

   public decimal Amount {get; private set;}

   public string Classfication {get; private set;}

   public Taxonomy(int id, decimal amount, string classification)
   {
      // validation
      Id = id;
      [..]
   }
}


class SaleAggregate : AggregateRoot
{
   private List<Taxonomy> _taxonomies;

   public int Id {get; private set;}
   
   public Invoice Invoice {get; private set;}

   public IReadOnlyCollection<Taxonomy> Taxonomies => _taxonomies.AsReadOnly();

   public SaleAggregate(int id, string number, DateTime date, decimal 
           taxableAmount, decimal vatAmount)
   {
       _taxonomies = new List<Taxonomy>();
    
       // I prefer to pass ALWAYS primitive types to not rely on valueObject
       // validation
       Id = id;
       Invoice = new Invoice(number, date, taxableAmount, vatAmount)
       [..]
   }

   public void AddTaxonomy(int id, decimal amount, string classification)   
   {
       // validation
       _taxonomies.Add(new Taxonomy(id, amount, classification);
   }

   public void UpdateTaxonomy(int id, decimal amount, string classification)
   {
       // validation
       var entity = _taxonomies.FirstOrDefault(p=> p.Id == id);
       entity.Amount = amount;
       entity.Classification = classification;
   }

   public void RemoveTaxonomy(int id)
   {
       // validation
       var entity = _taxonomies.FirstOrDefault(p=> p.Id == id);
       _taxonomy.Remove(entity);
   }

   public void UpdateVatAmount(decimal vatAmount)
   {
       // validation
       Invoice = new Invoice(Invoice.Number, Invoice.Date, 
                    Invoice.TaxableAmount, vatAmount);
   }
}


Again: this is my vision about Aggregate, Entities, and ValueObject, other developers reading this can feel free to correct me.

Related