As you already concluded in your comment, the graph should be the only aggregated root.
There is a difference between aggregates and aggregate roots. In your example, both Graph and Node are aggregates, but the object responsible for managing the entire population is Graph. So this is the root.
The easiest way to get an idea if an object is an aggregated root is to ask yourself:
Does it make sense to have only this object, separated from its parent?
If there are no answers, then this is probably not the cumulative root. For example, a single Node is probably of little use if it is not part of the parent graph. This is why you usually only have storages for aggregate roots; so that you do not have access to objects that are not part of their corresponding aggregate root.
Now about invariants. You stated that (my attention):
all [Node] names are unique within their parent [Graph]
Basically you answered your question. In the context of a single Node, it makes no sense to say that his name is unique. But in the context of the graph, he does, because he is an invariant of the Count, not Node. Therefore, the graph is responsible for protecting this invariant .
As for the "divine aggregate root", it often has one common aggregate root from a global business point of view. But an important aspect of DDD is the identification of various contexts within the system. Your system may have a high-level root containing many Graph objects. In this high-level context of managing your graphs, you probably are not even interested in the low-level Link objects in the chart.
It is important that you model the domain objects according to context. This has been one of the most important things that I have realized in the last few months. Most people know about DDD because of repositories, or perhaps because of objects and value objects, but they are not as important as limited contexts.
Despite the fact that there is only one “Something” business concept, it’s great to have several models that represent this “Something” concept, one implementation for each context. One implementation may be an aggregate root, while another implementation is part of a larger aggregate, depending on the context.
Common software mantras consist in reusing code, DRY, and the like, so at first it was wrong to have several classes that represent the same business concept. But as soon as I was able to release this feeling and understand that each implementation has its own responsibilities, this simplified the situation :)