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.
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 domain driven design, client talk to aggregate root directly to implement the business logic, aggregate root could be created by factory or loaded from repository. In order to implement the business logic, we have to create a new value object in some cases, so my question is:
Should aggregate responsible to the creation of value object, meaning that we encapsulate the creation of value object in aggregate root.
Can I create value object directly in application service without aggregate?
Can I create value object directly in application service without aggregate?
Sure, go right ahead. That's a pretty common pattern - for example, in a client/server architecture, the client will send a message to the application inviting some action (command/query) on the domain model. That message will be designed to cross process boundaries - in other words, it's a data transfer object. The application component will take that message, and re-express it as value objects that will be understood by the domain.
Example: a query of the domain might ask for details of some Account, which is identified by a UUID. So the initial DTO might be a string. Application layer throws that string into a JSON parser, then finds the account id argument (which is "just data"), and use it to create an AccountId value object (which the domain will recognize).
Part of the point of value objects is that equal objects freely substitute for one another. Nobody cares which 7 you have.
we encapsulate the creation of value object in aggregate root.
Your aggregate code is likely to create value objects too. Changes to your aggregate's state will often require creating/computing new values. Nothing wrong with that.
Quotes are from DDD: Tackling Complexity in the Heart of Software ( pg. 150 )
a)
global search access to a VALUE is often meaningles, because finding a
VALUE by its properties would be equivalent to creating a new instance
with those properties. There are exceptions. For example, when I am
planning travel online, I sometimes save a few prospective itineraries
and return later to select one to book. Those itineraries are VALUES
(if there were two made up of the same flights, I would not care which
was which), but they have been associated with my user name and
retrieved for me intact.
I don't understand author's reasoning as for why it would be more appropriate to make Itinierary Value Object globally accessible instead of clients having to globally search for Customer root entity and then traverse from it to this Itinierary object?
b)
A subset of persistent objects must be globaly accessible through a
search based on object attributes ... They are usualy ENTITIES,
sometimes VALUE OBJECTS with complex internal structure ...
Why is it more common for Values Objects with complex internal structure to be globally accesible rather than simpler Value Objects?
c) Anyways, are there some general guidelines on how to determine whether a particular Value Object should be made globally accessible?
UPDATE:
a)
There is no domain reason to make an itinerary traverse-able through
the customer entity. Why load the customer entity if it isn't needed
for any behavior? Queries are usually best handled without
complicating the behavioral domain.
I'm probably wrong about this, but isn't it common that when user ( Ie Customer root entity ) logs in, domain model retrieves user's Customer Aggregate?
And if users have an option to book flights, then it would also be common for them to check from time to time the Itineraries ( though English isn't my first language so the term Itinerary may actually mean something a bit different than I think it means ) they have selected or booked.
And since Customer Aggregate is already retrieved from the DB, why issue another global search for Itinerary ( which will probably search for it in DB ) when it was already retrieved together with Customer Aggregate?
c)
The rule is quite simple IMO - if there is a need for it. It doesn't
depend on the structure of the VO itself but on whether an instance of
a particular VO is needed for a use case.
But this VO instance has to be related to some entity ( ie Itinerary is related to particular Customer ), else as the author pointed out, instead of searching for VO by its properties, we could simply create a new VO instance with those properties?
SECOND UPDATE:
a) From your link:
Another method for expressing relationships is with a repository.
When relationship is expressed via repository, do you implement a SalesOrder.LineItems property ( which I doubt, since you advise against entities calling repositories directly ), which in turns calls a repository, or do you implement something like SalesOrder.MyLineItems(IOrderRepository repo)? If the latter, then I assume there is no need for SalesOrder.LineItems property?
b)
The important thing to remember is that aggregates aren't meant to be
used for displaying data.
True that domain model doesn't care what upper layers will do with the data, but if not using DTO's between Application and UI layers, then I'd assume UI will extract the data to display from an aggregate ( assuming we sent to UI whole aggregate and not just some entity residing within it )?
Thank you
a) There is no domain reason to make an itinerary traverse-able through the customer entity. Why load the customer entity if it isn't needed for any behavior? Queries are usually best handled without complicating the behavioral domain.
b) I assume that his reasoning is that complex value objects are those that you want to query since you can't easily recreate them. This issue and all query related issues can be addressed with the read-model pattern.
c) The rule is quite simple IMO - if there is a need for it. It doesn't depend on the structure of the VO itself but on whether an instance of a particular VO is needed for a use case.
UPDATE
a) It is unlikely that a customer aggregate would have references to the customer's itineraries. The reason is that I don't see how an itinerary would be related to behaviors that would exist in the customer aggregate. It is also unnecessary to load the customer aggregate at all if all that is needed is some data to display. However, if you do load the aggregate and it does contain reference data that you need you may as well display it. The important thing to remember is that aggregates aren't meant to be used for displaying data.
c) The relationship between customer and itinerary could be expressed by a shared ID - each itinerary would have a customerId. This would allow lookup as required. However, just because these two things are related it does not mean that you need to traverse customer to get to the related entities or value objects for viewing purposes. More generally, associations can be implemented either as direct references or via repository search. There are trade-offs either way.
UPDATE 2
a) If implemented with a repository, there is no LineItems property - no direct references. Instead, to obtain a list of line items a repository is called.
b) Or you can create a DTO-like object, a read-model, which would be returned directly from the repository. The repository can in turn execute a simple SQL query to get all required data. This allows you to get to data that isn't part of the aggregate but is related. If an aggregate does have all the data needed for a view, then use that aggregate. But as soon as you have a need for more data that doesn't concern the aggregate, switch to a read-model.
Based on the following definitions from Domain-Driven Design: Tackling Complexity in the Heart of Software,
An aggregate is:
A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the AGGREGATE, designated as the root. A set of consistency rules applies within the AGGREGATE'S boundaries.
I don't think the Aggregate root should hold a reference to the repository. Since the Aggregate root is the only one that should be holding references to its entities and aggregates, they should be private.
How can my repository persist and restore this private data ?
Edit:
Let's take the classic Order, OrderLines example.
An order is the Aggregate root.
It's lines are Entities.
Since the Aggregate root(order) is the only object allowed to hold references to its entities (order lines), I do not understand how would I persist order lines from the repository.
As far as I understand the aggregate root, it must be the place to access all the entities inside it's scope. That means, as long as traditional ORM is used, that you can access the OrderLines throug the Order.
Further it is not forbidden for anyone to grab a reference to the entitiy inside the root, but these references must be volatile (i.e. short lived) and you must obtain the rerefence via the aggregate root.
In terms of DDD you will use a repository to hide data access, the factory might in turn use a factory to assemble the object. The facotry knows well about the internal structure of the object and must be able to build up a new object or restore one from the data the repository hands over.
Perhaps you might also look into CQRS + Event Sourcing which provides a different approach to persisting entities.
Well, most folks consider the repository to be a logical feature of hte aggregate root (since there's only one per aggregate, in traditional DDD), so it does & should have access to the orderlines.
If you really want them to be private, though, you would need to resort to reflection, or else have the aggregate root entity return them in some persistable fashion (perhaps w/ an internal call of some kind).
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?