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.