DDD - forced invariants with small total roots

This is my first attempt at DDD, and I have a problem with aggregate design.

My application contains 3 objects; Graph, Node, Link. Each of these objects has a name property that can be changed by the user (which, in my opinion, makes the "name" unusable as an Entity identifier). The graph contains a set of nodes, and Node has a set of outbound links (for this purpose, it is safe to ignore incoming links). Each Node can only be associated with one schedule at a time (but can be moved between schedules), and similarly, each link can be associated with only one Node at a time (but can be moved).

The invariant (s) I'm trying to provide is that all Entity names are unique in their parent collection. With the architecture described above, the invariant is in the actual collections, so I decided that the owners of the collections (Graph and Node) should be like Aggregate Roots.

The problem I am facing is how to apply the name invariant on Node now? On Link is easy because it is hidden inside Node AR and therefore Node can confirm that all renaming / moving Link do not violate this invariant. But, as far as I see, there is nothing that could prevent the direct renaming of Node, which could destroy the invariant. Finite consistency is not an acceptable option here; it must be a true system invariant.

The approach I'm considering is to have Node.Rename () actually enforce the invariant, but I am concerned that this is due to what it is looking inside of its parent graph to check if the renaming is valid. This is not “correct” - it seems that Graph should be the only one to ensure that this name restriction is limited and that Node knows nothing about it at all.

Hope this makes sense, and I look forward to hearing people's thoughts.

Edit: The above domain model is a simplified subset of the entire domain. Too complicated for all objects to be stored within the same AR .......

+8
invariants domain-driven-design aggregateroot
source share
2 answers

The solution I found for this problem arose due to the CQRS approach. I found a great introduction to CQRS here .

In my model, "write"; The Node graph and the link are all aggregated roots, but the names are completely controlled by the parent collection. Thus, in the record model a Node has no idea that his own name (which means that name updates must go through his own graph). In the corresponding “read” model, the name is directly related to Node (since this is useful for display).

The advantage of this solution is that it allows me to keep my ARs small, but since the "name" information is stored inside the parent collection, I have no problem saving cross-aggregate invariants.

+6
source share

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 :)

+18
source share

All Articles