Q:

.NET DDD Domain Pattern with Grandchildren

I would like to implement DDD Pattern in my .NET Core Domain Model.
Here is an example of the Aggregate Root with Aggregate Child Entity as well as Aggregate Grandchild Entity:
public class Supplier
    : Entity, IAggregateRoot
{
    private string _name;

    private readonly List<Catalog> _catalogs;
    public IReadOnlyCollection<Catalog> Catalogs => _catalogs;

    protected Supplier() { _catalogs = new List<Catalog>(); }

    public Supplier(string name)
    {
        _catalogs = new List<Catalog>();
        _name = name;
    }

    public void AddCatalog(string name)
    {
        var catalog = new Catalog(name);
        _catalogs.Add(catalog);
    }

    public void AddCatalogItem(int catalogId, string name)
    {
        var catalogItem = new CatalogItem(name);
        _catalogs.Single(c => c.Id == catalogId).AddCatalogItem(catalogItem);
    }
}

And here the code for the Aggregate Child:
public class Catalog
    : Entity
{
    private string _name;

    private readonly List<CatalogItem> _catalogItems;
    public IReadOnlyCollection<CatalogItem> CatalogItems => _catalogItems;

    protected Catalog() { _catalogItems = new List<CatalogItem>(); }

    public Catalog(string name)
    {
        _catalogItems = new List<CatalogItem>();
        _name = name;
    }

    public void AddCatalogItem(CatalogItem catalogItem)
    {
        _catalogItems.Add(catalogItem);
    }
}

And the Aggregate Grandchild:
public class CatalogItem
    : Entity
{
    private string _name;

    protected CatalogItem() { }

    public CatalogItem(string name)
    {
        _name = name;
    }
}

Is this the right way to accomplish DDD Pattern?
Or does this break DDD rules that I am not aware of?

A:

It doesn't break any DDD rules per se - but it is an approach that is often discouraged, for a few reasons:

DDD encourages you to model the domain based on the operations, processes & rules that your system needs to support while managing changes to state.

The relationships you have modelled may exist in the real world - and they may help for visualising the data - but are they really necessary for your model?

Good practice in DDD is to design small aggregates.  An aggregate is truly a 'consistency' boundary - the larger you make your consistency boundary you have a few consequences:

Most operations don't relate to all the data - but all the data must be transactionally locked during each operation
In collaborative multi-user domains this can lead to concurrency issues where either users are blocked due to the data they want to manipulate being locked unnecessarily

Why might such relationships be useful to model?

If you had a rule (invariant) that MUST have transactionally consistent data across all the child aggregates, e.g.:

Must be at MOST 4 catalogs per supplier - to enforce this rule, you must have transactionally consistent list of catalogs in the supplier
The sum of the price  of catalogue items in a catalogue must not exceed $X - again, you need all the items in a transactionally consistent way

Alternatives
So - if you don't have any such rules, and you've been convinced to create 'small' aggregates, what can you do?

Each entity becomes it's own aggregate
Link entities by reference.  In this case:

Supplier would not have a list of catalogues
Catalog would have a 'SupplierId' to link the catalogue to the supplier, and facilitate obtaining the list of catalogues by supplier
Catalog would not have a list of CatalogItem
CatalogItem would have a 'CatalogId' to link to the catalog - again, facilitating obtaining the list of items by catalog

The implication of this approach is that you will not have transactional consistency across the aggregates - but unless your rules demand it, you don't need it, so why enforce it?
Do you really need transactional consistency?
Another piece of advice that DDD encourages is to challenge demands for transactional consistency - is it really really necessary? 
Using the trivial example above - what would happen if a supplier ended up with 5 catalogs? would the world break? Even if you did need that that rule, does it need to be transactionally consistent?  With smaller aggregates, you could:

Include client-side validation to prevent the operation if the supplier already had 4 catalogs
Publish an event when a catalog is created, which triggers a check and compensating action - if the handler determines that the supplier now has 5 catalogs, raise an alert, or update the supplier state until the user removes a catalog, or automatically expire the oldest catalog - whatever makes the most sense in your domain

Summary
The recommendations are:

Design small aggregates
Embrace eventual consistency

