Say I have a "Directory" bounded context with 2 aggregate roots. Company and Person. Company has a collection of child entities "Position" that hold the IDs of a person aggregate with some extra value data.
All good.
Now we go and add a "Article" bounded context with an aggregate root JobAriticle. This needs a Contact value object which is mapped from a company position.
So knowing that you should only reference aggregate roots what should I do? Assume there are invariants on the Company to Position relationship so I don't want to split the aggregate. Is it OK to map a Position through the anti corruption layer with both the company and position ids? Or do I need to try and break apart the company aggregate.
If Position is a meaningful concept in the Ubiquitious Language of the "Article" bounded context then Position should be either a ValueObject or Entity in the "Article" bounded context.
If you're saying that Contact is the meaningful concept in the "Article" bounded context, but that it needs to somehow correlate with a Position in the "Directory" bounded context, then you need to think about what is the purpose of that correlation:
Do you need to verify that a position exists before creating a contact on a JobArticle?
Is there some shared data that will be kept in sync between a Position and a Contact?
What happens if a Position is archived/deleted or otherwise rendered out of service - do you have to do something to all the contacts?
If you need to verify that a Position exists before creating an associated Contact, then implicitly Position plays a role in the responsibilities of the Article context - so I would be tempted to create a new Position entity in the Article context and keep it in sync.
Either way, to correlate something in the Article context with a corresponding entity in the Directory context, you could create a ValueObject in the "Article" context called PositionCorrelation which would have two properties:
CompanyId (globally unique)
PositionId (locally unique within a company)
The rule that aggregates should only reference other aggregate roots doesn't mean that you can't also provide information with which to identify entities within the aggregate. It just means that if you want to interract with the other entity, you should do it via the aggregate root, which means you must at least have the aggregate root Id. If you then use a local Id to ask the Company to do something to one of it's positions, that's fine.
But - be aware, that by following this approach, you are introducing the term "Position" into the "Article" bounded context, which may introduce a name collision if the word "Position" means something else in the "Article" context - e.g. perhaps it means the position within an article (paragraph number etc.). If this is the case, you need to think carefully about what to call the cross-context identifier.
One approach could be that if Position has a one to one map with Contact, then you could have your two properties be:
CompanyId
CompanyContactId
And when keeping in sync at the Anti-Corruption layer (ACL) when integrating between the contexts, define CompanyContactId and PositionId (local within Company) values as being synonomous. This keeps each UL internally consistent and defines the correlation between the two in an ACL.
Related
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
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.
I just started on DDD and encounter the term aggregate roots.
My current understanding is that this is kind of a parent entity that hold reference to other complementary entity. Example : aggregate roots will be Employee that also contain position, shift, gender, and salary.
My first question will be whether this understanding is correct ?
Secondly, I get an impression that repository is defined only for each aggregate. Yet, it puzzles me how we could retrieve information regarding other entity (Ex: list of positions or shift type) ?
Thank you,
Aggregates are consistency boundaries to enforce invariants. This means that the entities and objects inside the aggregate must remain consistent together with regards to the business rules.
http://dddcommunity.org/library/vernon_2011/
http://martinfowler.com/bliki/DDD_Aggregate.html
https://lostechies.com/gabrielschenker/2015/05/25/ddd-the-aggregate/
Secondly, I get an impression that repository is defined only for each aggregate. Yet, it is puzzle me how we could retrieve information regarding other entity (Ex : list of positions or shift type) ?
You can have a separate read model over your data if you choose to do so and it makes sense that the business wants to view the data in a different way. The consistencies you need to enforce when you are writing data do not apply on the read side. CQRS is the pattern to help with this - you separate your write side from your read side.
https://lostechies.com/gabrielschenker/2015/04/07/cqrs-revisited
I have a domain model
Customer - Aggregate root - because an order can't exist without a customer
Order - entity
OrderStatus - value object
In my form I need a list of all OrderStatuses.
Should I fetch an empty customer entity(AR) from repository with an empty order entity which is containing a list of all OrderStatuses? This is awkward.
Well, it always depends on your problem domain, but lacking further info, I would say you probably need to break your modeling a little bit.
Even though an Order can't exist without a Customer, it will not be a child entity under the Customer AR. You need to introduce the notion of Bounded Contexts.
Customer would be the AR of one BC, while Order would be the AR of its own BC.
In that case, you would reference Customer from Order with a CustomerId property (not with an object reference) because they belong to different contexts, and as such they could even live in separate microservices, in separate databases.
You see where I'm going: it makes no sense to fetch an empty Customer, with an empty Order (or list of Orders) just to reach a list of Order Statuses.
Even if Order and Customer did belong to the same BC, OrderStatus is Reference Data, and would be better represented by an enum type (or better, with the Enumeration Pattern).
Have a look at this additional info:
Reference data as code
Entities, Value Objects, Aggregates and Roots
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?