I have some doubts about entity relationship modeling. For example I have User entity, each user can have a limited number of Task objects. Task is just a simple Value Object. Here all is clear, since user can interact (open, close, change) tasks through User entity that acts as aggregate root.
Now I need to introduce another entity Manager, a manager should have access to all the tasks regardless of who owns them. He can also create tasks and pass the ownership to some user. And I'm a bit lost here how should I model this?
Should I make Task a separate entity/aggregate and keep OwnerId as a reference to a User?
I would appreciate some ideas that could guide me in the right direction.
I have a task management app and I model Task as an aggregate. User is another aggregate.
I model every relationship with a user id reference in Task. For example I see you have ownership and creation. Then I should have in Task entity:
CreatedBy: the id of the user who created the task.
OwnedBy: the id of the user who is the task owner.
I have also the concept of assignee, so I have:
AssignedTo: the id of the user who has to perform the task.
Regarding to Manager, I see it as a role more than a user. A user with the manager role could create tasks, change their owners, etc.
It all depends on your business rules but that's the idea.
The question you should ask are
can a task be referenced from someone else other than its creator (answer appears to be yes now)
can a User or manager have an unbounded set of tasks associated?
can a task be reassigned to different users?
If these 3 (or other that now I don't have on the tip of my fingers) question have a positive answer, than you domain concept is an aggregate
In your case, your Task concept has all the requirements to be handled as an entity, and the entity as an aggregate.
Why entity?
Tasks, after all, have their own id. You can edit the description of a taks from "Zwip under the carpet" to "Swipe under the carpet" because of a typo, but this doesn't change what you're about to do, and this fits with definition of Entity.
Why Aggregate?
Then it's clear that the above questions have a "yes" as an answer, they can be managed from users or managers, there is no such limit like "a user has around 3 tasks for the entire life[see note]" and this means that you have to change the task status in a separate transaction boundary and fetch them by their gobal id. You don't want to load all the tasks beloning to a certain user, I bet
...but the beauty of DDD approach is that nobody can know this better than the guy/girl who knows how things work in the company that is building this software
[note] notice that the dscrimitator here is not how many tasks the user is currently doing, but all created the tasks associated with all created the users
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
I am trying to utilize some DDD approaches in the app that allows guest purchase. While it looks easy, I got a bit confused and asking for your DDD advice.
The app has several bounded contexts and we are looking at 3 of them:
Customers (customers manage their user settings here, authentication, also admin can potentially create users)
Sales (orders)
Billing (charging customers for one-off payments and subscriptions)
The user story:
As a guest I want to order the product to do something.
This is one form and on checkout he/she will be asked for email and password. We need to create account on this step, but based on business logic this fits to Sales context - guest makes an order. We actually need to do simple steps:
Create user with ACTIVE status
Store billing details
Create order
Charge user later (event handled by Billing)
The confusion here is that it requires creating a user first. It looks more naturally to create it in customers, but probably it’s wrong? User can not sign up in any other way - only by placing an order, but admin can create a user manually. Based on different system events(from different contexts), the Customer context may change the user status based on special logic that is placed into Customer domain. Is there any safe way for sharing this User status logic between different contexts (while creating User in Sales we need that status enum class)? Does this logic for placing order look ok? Could you please recommend another approach if you think that this one is wrong?
Status is DDD at its worst. Including a status field is 1) lazy, and yet 2) very convenient. Yes, one of those design trade offs.
When you assign a status or read a status you are ignoring or sublimating significant business logic and structure for your domain. When “status” changes some very significant changes could occur in your domain... way beyond changing a status property.
Throw status out and instead consider some concepts: a CasualShopper or Guest (no purchases, browsing for products), a PotentialNewShopper (someone adding things in their basket who you’ve never seen before), and your usual Customer (which should probably be subdivided based on their current activity).
With this modeled, you can attach behaviors directly to each of these objects and “status” itself is sublimated into a richer DDD model. A common DDD mistake is not creating a transactionally-significant object (e.g. a Potential Shopper role) for some static/non-temporal object (e.g. a person).
From here you may decide you need a few bounded contexts; perhaps PotentialCustomers and EstablishedCustomers. In each the set of domain transitions are different and can be encapsulated rather than externalized.
So...
With that out of the way it looks like you have a Customer BC and a PossibleCustomer BC. You can safely do what you need to do in the latter now that it is self-contained.
Oh, but that may affect billing! and ordering!
True. That may mean new BCs or new objects in those BCs such as ProvisionalPayment and UnauthenticatedOrder. I’m spitballing a bit now...
My point is you have events that can transition between states rather than encoding those states, and as such you can attach the behaviors you need and persist them as needed in some physical store, that is likely partitioned in a way suitable to your DDD.
Doing all this work means not sharing unsafe status but sharing safe projections of relevant objects only.
Jumping into implementation briefly and anecdotally, developers are loath to store “temporary” state. “Temporary” state is OK to store and necessary when you’re modeling a domain without that cruddy status field.
You probably should ask yourself first whether you got the Bounded Contexts right.
In my opinion you have the following BCs
Identity and Users
Sales and Billing
consider this: the same person is a User in the first context but a Customer in the latter. so you have two views on the same real world entity which suggests that you have two bounded contexts where this entity means different things.
your bcs sound more like modules in the Sales and Billing context.
If you agree, then the control flow for your problem may be simplified where an entity in one context is created and the creation is propagated via event into the other. so the initial request could be handled by the Sales bc and the guest user handling would be propagated to Identity.
I have a question regarding the design of aggregates, as presented by Vernon, both in his articles in the DDD community (Effective Aggregate Design, Part 3) as well as on his book (Implementing Domain-Driven Design).
In there, he explores two possible approaches when designing BacklogItem and Task. In one of them Task is not its own Aggregate Root because it runs the "risk of leaving the true invariant unprotected", and the aggregate root is the BacklogItem
However, one of the other guidelines for designing aggregates roots is that access to an entity should only be done through the root itself. Which means that in order to get access to a Task in this approach one would have to now the BacklogItem it belongs to and ask for the backlog item. Normally, though one would just want to see the Tasks is assigned with, and not the backlog item.
In this case we will need to access the entity directly and not via the Backlog item. How does this sit with the proposed design? (I understand that this may be just an educational demo but how would someone have to think this if this was real life?)
Thanks in advance for any answers
In this case we will need to access the entity directly and not via the Backlog item. How does this sit with the proposed design?
That depends: "access directly" is under specified.
Bertrand Mayer's language of command query separation helps here. Queries leave the model unchanged; commands update the model.
Here's the key idea: the unique concern of the aggregate root is commands; any change to the state of the aggregate is achieved by sending a command to the root entity, which may at its discretion delegate the responsibility of changing the state to some other entity.
So if you are accessing the Task to query its current state, that's just fine. But getting the Task so that you can send commands to it directly? that breaks the rules. The aggregate root has the privilege of exclusive access to the commands of all of the entities within the aggregate.
The implication is that you would never invoke Task.estimateRemainingHours() directly; you would instead invoke some analogous method on the aggregate root. BacklogItem.logEstimateFromTeamMember(), perhaps, which would in turn decide which tasks need to be updated.
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"?).
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).