Root aggregate problem in domain driven design - domain-driven-design

I have two Entities Publisher and SocialAccount , both are independent and having relationship of Many to Many. Both are root Aggregates , now i cannot get social account through Publisher and I want to convert M To M relationship to 1 To M. So I Introduced another entity Registration , will have {PubID, SocID, CreateDate}. Now there is 1 To M relationship between Publisher and Registration and 1 to 1 between Registration and SocialAccount. so publisher will have
List <Registrations> _Registrations {get;set;}
But when I create aggregate boundaries, Publisher is my root and according to aggregate principle, only root aggregate will hold reference to another root aggregate. But here Registration hold reference.
So do i am violating aggregate principle , because registration is connected Social Account entity.

Your use of aggregate concept appears to be incorrect. Objects within an aggregate can in fact hold references to other aggregates. The rule is an external object cannot hold a reference to something within an aggregate.
On the Registration object, you seem to have created it to avoid some aggregate to aggregate relationships. That is not why you create an object. If there is in fact a Registration in your domain, create it and model it. If it is not in your domain, don't add it just to traverse some path.
Having added Registration, you say it cannot hold a reference to Social Account because it is part of Publisher. That is not the rule, but more important how did Registration suddenly become part of the Publisher aggregate anyway? By virtue only of Publisher having a Registration collection?
An aggregate is a group of objects that are treated as one unit for maintaining state and invariants. Existence of a relationship by itself does not confer membership in an aggregate.
But look at the other side now. Registration is 1 to 1 with Social Account. And if we remove a Social Account does it ever make sense to still have a Registration with a Publisher? If not then Registration probably is in fact part of the SocialAccount aggregate instead. That is why we create aggregates - to ensure objects and their relationships are always valid after a state change. If the change of state of removing a SocialAccount includes removing all Registrations associated with that account, we would want to include it in the aggregate to enforce that rule.
Now you have indeed violated the "aggregate rule" - you have an external relationship from Publisher to an object, Registration, that is an internal part of the SocialAccount aggregate.
These concepts are more than just rules, they have reasons. You need to review what aggregate really means, understand what the rules actually say and what they really mean, why they exist in the first place. Then reevaluate your relationships and aggregate definitions accordingly.

First we need an abstraction for encapsulating references within the model.
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes.
Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single, specific ENTITY contained in the AGGREGATE. The root is the only member of the AGGREGATE that outside objects are allowed to hold references to, although objects within the boundary may hold references to each other. ENTITIES other than the root have local identity, but that identity needs to be distinguishable only within the AGGREGATE, because no outside object can ever see it out of the context of the root ENTITY.
what you think about it Ssyphus?

Related

DDD - How to form Aggregates where Entities have to reference non-root Entities

I have some Entities and I am trying to follow Domain Driven Design practices to identify Aggregates. I somehow cant do this because I either break the rule of Entities not being allowed to reference non-root Entities of other Aggregates, or I cant form Aggregates at all.
I have the following Entities: Organisation, JobOffer, Candidate, and JobApplication.
An Organisation creates JobOffers but may only have a limited amount of active JobOffers.
A Candidate creates JobApplications but may only have a limited amount of active JobApplications.
A JobApplication references a JobOffer that it is meant for.
Based on that I have to know how many JobOffers an Organisation has before I can create a new one (enforcing limits), I assume Organisation should be an Root-Entity that owns JobOffers. The same applies to Candidates and JobApplications. Now I have two Aggregates: Organisation with JobOffers and Candidate with JobApplications. But... I need to reference JobOffer from JobApplication... and that breaks the rule that I cant reference non-Root-Entities.
I have looked for and found similar questions on this forum but I somehow still cant figure it out, so sorry in advance - I appreciate any help.
I general, you should avoid holding object references to other aggregates but rather reference other aggregates by id. In some cases it can be valid to reference some entity within in another aggregate, but again this should be done via id as well.
If you go this way you should reference a composite id. Aggregates are meant to depict logical boundaries and also transactional boundaries. Child entity ids which are modelled as part of the aggregate only need to be unique inside the boundaries of that aggregate. This makes it a lot easier to focus on stuff just inside those boundaries when performing actions in your system. Even if you are using UUIDs (or GUIDs), if you really need to reference a child entity of another aggregate - let's say you have good reasons for that - you should model the id graph via the aggregate root which means always knowing the id of the other aggregate in combination with the id of the entity you are interested in. That means referencing a composite id.
But: whenever I think I need to reference a child entity of another aggregate root at first I investigate this more deeply. This would mean that this child entity might be important as a stand-alone entity as well.
Did I miss to discover another aggregate root?
In your case, looking at your domain model diagram, I suspect JobOffer should be an aggregate on its own. Of course I don't know your domain but I can at least guess that there might be some transactions performed in your system allowing to mutate job offers on its own without requiring to consider organization specific business invariants. If this is the case, you should rethink the domain model and consider making JobOffer an aggregate root on its own. In this case your initial problem get's resolved automatically. Also note that modelling job offers as aggregates can make actions performed on organizations simpler as well as you do not need to load all the job offers for that organization when loading the organization aggregate. This might of course not be relevant in your case and really depends on the maximum amount of job offers for an organization.
So I think, depending on your business requirements and domain logic invariants I would recommd one of the folllwing two options:
Reference the foreign child entity only through a composite id including the id of other the aggregate + the child entity id (e.g. by creating some value object that represents this reference as a strong type)
Make JobOffer an aggregate on its own if the mentioned considerations hold true in your case

DDD - Should entities create entities?

In my domain I might have a entity called User that represents a person and Tenant, an entity that represents a business.
One might think that this is a administrative user, and as such, it should be able to register new users, which in terms of code would be translated to basically 3 forms:
-adminUserInstance.CreateUser(newUserDTO);
-tenantInstance.CreateUser(newUserDTO)
-newUserInstace.SelfSave()
Being newUserInstace.SelfSave() a necessity because of self-registration, does the other two make sense? Should i keep only the self registration and turn to a model of affiliation, where the same user can be part of multiple tenants?
In broader terms: should Questions/Answers be created by Users or self created and then tied to a given User? and on even broader terms: Should entities hold the knowledge to create other (not any-others) entities or the entities should be able to create themselves and have the "requester", for example, call a domain service that will bundle them up?
In broader terms: should Questions/Answers be created by Users or self created and then tied to a given User? and on even broader terms: Should entities hold the knowledge to create other (not any-others) entities or the entities should be able to create themselves and have the "requester", for example, call a domain service that will bundle them up?
Udi Dahan's technical guidance was that you should always get an entity, and use it to create the new entity. "Customers don’t just appear out of thin air."
That said, creating aggregate root entities is weird; not "wrong", but it deviates from the usual patterns of dispatching commands to the aggregate you are going to modify.
Entities can be created by entities but only inside the same aggregate. So, if an aggregate creates an entity then that entity is a nested entity; it cannot be referenced outside the aggregate's boundary. You put the creation of nested entities inside the aggregate because the aggregate needs to enforce some invariants and this is a decision to make when you design your Aggregates.
On the other hand, aggregate roots (AR) are created by the client code (an Application service in most cases) but the AR enforces it's own invariants! So, in PHP code this should look like this:
//somewhere in an Application service
function createUser($id, $username, $password)
{
$user = new User(); //an AR should always be new-able, i.e. with not injected dependencies
$user->registerNew($id, $username, $password); //here the invariants are enforced
$this->userRepository->addOrSave($user);
}
newUserInstace.SelfSave()
An aggregate does not "save" (persists) itself. That is the job of an Application service. Anyway, "save" does not seem to be from your ubiquitous language, you need a more proper name ("register"?).

DDD: Choosing aggregate root

In my case I have two main concepts: User (main citizen of the system) and Group.
Group has two sub-collections: ranks and roles. Without a group, ranks and roles have no meaning.
When User is assigned to the Group, we also have to pick 1 Role and 1 Rank and assign them to this relationship between User and the Group.
Diagram
Question:
How much aggregate roots do I have here? From the user side its obviously a user (main concept of the system), but what about its relationship with group? AFAIK its forbidden by rules of DDD to reference entities outside of aggregate root.
AFAIK its forbidden by rules of DDD to reference entities outside of aggregate root.
Well, I would't say it's "forbidden by rules of DDD"... Some times you have no choice. I have to consider the size of the "entities' collection" associated to the Root's Aggregate. Sometime you can maintain the association in the same aggregation and use some kind of "lazy load" to avoid resource consumption. The Vernon's iDDD book[1] has some advises and use cases around this specific case. Take a look on his blog post[2]
[1] https://www.amazon.com.br/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577
[2] https://vaughnvernon.co/?p=838
You have at least the following options, depending on your business requirements regarding consistency:
You have 5 aggregate roots: User, Group, Rank, Role and UserAssignment. The last one must protect the invariant "we also have to pick 1 Role and 1 Rank". For lifetime management you use eventual consistency between ARs. For example, when you delete a Group you must delete also the orphan Ranks, Roles and UserAssignments.
You have User (with UserAssignment as nested entity) and Group (with Role and Rank as nested entities). You have strong consistency inside ARs (when you delete a user all its asignments are also deleted) and eventual consistency between User and Group.
What should you use? Only you could decide. For example, if you choose the first option and delete a user then there could be a delay of seconds/minutes/hours before its assignments are also deleted.
Strong consystency should be used to protect only real business invariants because it is not cheap.
P.S. if you need to hold a reference to a nested entity from another AR then you should reconsider your aggregate roots boundaries because your design is most probable wrong.
I'm going to change some of the words and we'll see if it helps (hypothetical):
I have an Order and a Product. When I add a Product to an Order I have to choose a Store and a Colour.
How would you model this?
Colour may very well be a Value Object but Store is not. I would opt for an OrderItem Value Object which contains a Colour Value Object and a StoreId value to capture the relationship. An Order would contain a list of OrderItem entries.
Removing Colour entries is fine since we have denormalized that bit into the OrderItem. We could have another Value Object represent the Store but typically we would not delete the store or have some processing to handle a deletion or, even more typical, use referential integrity constraints to prevent deleting a used Store.
If you consider ever deleting the Order only the OrderItem association is also deleted.
In your case User and Group are probably Aggregate Roots and I'd add a UserGroup (or UserAssignment as Constantin used). The UserGroup contains the association and related bits. You would have to identify the real domain structure though.

Repository within domain objects

I have seen lot of discussions regarding this topic but i couldn't get a convincing answer. The general advice is not to have repository inside a domain object. What about an aggregate root? Isnt it right to give the root the responsibility to manipulate the composed objects?
For example, i have a microservice which takes care of invoices. Invoice is an aggregate root which has the different products. There is no requirement for this service to give details about individual products. I have 2 tables, one to store invoice details and other to store products of those invoices. I have two repositories corresponding to the tables. I have injected product repository inside the invoice domain object. Is it wrong to do so?
I see some mistakes according to DDD principles in your question. Let me try to clarify some concepts to give you hand.
First, you mentioned you have an Aggregate Root which is Invoice, and then two different repositories. Having an Aggregate Root means that any change on the Entities that the Aggregate consists of should be performed via the Aggregate Root. Why? That's because you need to satisfy some business rule (invariant) that applies on the relation of those Entities. For instance, given the next business rule:
Winning auction bids must always be placed before the auction ends. If a winning bid is placed after an auction ends, the domain is in an invalid state because an invariant has been broken and the model has failed to correctly apply domain rules.
Here there is an aggregate consisting of Auction and Bids where the Auction is the Aggregate Root.
If you have a BidsRepository, you could easily do:
var newBid = new Bid(money);
BidsRepository->save(newBid);
And you were saving a Bid without passing the defined business rule. However, having the repository just for the Aggregate Root you are enforcing your design because you need to do something like:
var newBid = new Bid(money);
auction.placeBid(newBid);
auctionRepository.save(auction);
Therefore, you can check your invariant within the method placeBid and nobody can skip it if they want to place a new Bid. Afterwards you can save the info into as many tables as you want, that is an implementation detail.
Second, you said if it's wrong injecting the repository into a Domain class. Here a quick explanation:
The repository should depend on the object it returns, not the other way around. The reason for this is that your "domain object" (more on that later) can exist (and should be testable) without being loaded or saved (that is, having a dependency on a repository).
Basically your design says that in order to have an invoice, you need to provide a MySQL/Mongo/XXX instance connection which is an infrastructure detail. Your domain should not know anything about how it is persisted. Your domain knows about the behavior like in the scenario of the Auction and Bids.
These concepts just help you to create code easier to maintain as well as help you to apply best practices such as SRP (Single Responsibility Principle).
Yes, I think it is wrong.
Domain should match real business model and should not care how data is persisted. Even if data internally are stored in multiple tables, this should not affect domain objects in any way.
When you are loading aggregate root, you should load related entities as well in one go. For example, this can easily be achieved with Include keyword in Entity Framework if you are on .NET. By loading all the data you ensure that you have full representation of business entity at any given time and you don't have to query database anymore.
Any changes in related entities should be persisted together with aggregate root in one atomic operation (usually using transactions).

Domain Driven Design and local identity in an aggregate

In Domain Driven Design there is an Aggregate Root that has reference to internal entities.
Aggregate Root is an entity with global identity (everyone able to use its id). Aggregate root has links to local objects (entities).
Assuming here that Entities are the Hibernate #Entities (let's say)
Let's say we have Aggregate Root "User" that has "Address" entity in it as an object (which is actually an entity as well)
The question is:
How is it possible to make local entities to be with local identity only. I mean, there is no any barriers that could prevent anyone to use local entities (like Address) by its IDs. (so then this identity is not local at all, but global). Then, what is the way to make it local?
Well i don't think this is a matter of a public field or property or some access restriction mechanism, the way i see it "local identity" means that objects outside of the aggregate boundary can't use that local identity in a meaningful or useful way (e.g. they can't use that identity to retrieve that object or persist it to the database or any other operation). That identity doesn't mean anything to the outside world and it is only unique within that aggregate. Another example, what guarantees you that objects outside of an aggregate boundary won't hold references to objects within (which violates one of the principles of aggregates), well nothing unless those objects are VALUE OBJECTS which might not be the case every time. If i want to put that in a few words: Don't create any public APIs that use identities of objects within an aggregate , this way you will make it clear to the developer not to use those IDs.
All entities, including the root, have an identity. The fact that only the identity of the aggregate root should be used "globally" is something that cannot be easily enforced by the code itself. In a relational database in particular, every table record will have some key, regardless of whether that record stores an aggregate root, and entity or a value object. As such, it is up to the developer to discern which database identities are part of the domain and which are not.
Entities within an aggregate root are supposed to only have local identity. For all intents and purposes the database table need not have a primary key. When the aggregate is hydrated the entities within the AR should be fetched based on their link to the AR. But even that FK need not be represented in the local entity since the connection is obvious based on the containment of the local entities with the AR.
Since most database systems will moan if there is no PK on a table so you could add one for the sake thereof but you can just ignore it in your entity design. So there would be no property for the PK in the entity. The only way someone could then get to that entity is by way of the DB since there should be no way in your code to do so.

Resources