How to propagate consequent changes inside the same Domain entity - domain-driven-design

In a Domain Driven Design implementation with CQRS and Domain events, when a Command's handler performs a "change" on a Domain entity, and this "change" also causes more "changes" inside the same entity, should all these "changes" be performed by the same Command handler, or is it better to create and send different Commands to perform each of the consequent changes on this entity?
For example, let's say my Domain entity is a Match which has a Minute and a Status properties, and an UpdateMatchMinuteCommand handler updates the Match's Minute value by invoking a class method of the entity. But based on some business logic, inside the class method, this Minute change causes also a change on the Status property of the Match. Shall both of these changes (Minute and Status) take place inside the same class method mentioned earlier?
Or is it more appropriate for the class method, after updating the Minute value, to publish a MatchMinuteChangedDomainEvent, which in turn when handled will send an UpdateMatchStatusCommand, which in turn when handled will invoke another class method of the Match entity to update the value of the Status property?
Which of the above approaches would be more suitable to propagate consequent state changes inside the same Domain entity?
Thanks in advance

I have the feeling that you're going to over complicate the problem. If it's part of the root entity, why should you split the changes in multiple commands?
Reusing Match, Minute and Status, there are (I think...) 3 different situations then:
Examining the domain, you find out that Match contains Minute and Status.
From the analysis you've got the logic that trigger the changes in both the properties of the entity, and per design (DDD) you've put it into Match. Hence, inside the function that update Minute you code also the eventual change of Status.
From the analysis of the domain you find that the Match contains Minute, but not Status, that belongs to another root entity.
This is a case, with 2 entities involved, where using multiple commands is a plausible solution. You update one, generating an event; the event is handled by an handler, that builds another command, which in turn performs the other change. Maybe you do it into a single transaction, or you use a saga; whatever you think it fits your needs.
The domain requires the property Minute into Match, but there're no domain requirements about Status.
Here the Status property it exists, but it's not part of the domain: it could be an extra property, that is used only in the UI, or something else, an helper field, used to perform faster queries. Anyway, Status cannot be inside the entity, because it's not present into the domain.
Given that 2 and 3 are not what you've described, it remains only 1. Hence, I think that both should be changed inside the same action (or function).
A final consideration. You're going to:
build a model of the domain based on your business rules, and
build it following the DDD indications
That means that:
the domain logic should (or must...) stay inside the domain object
nothing that belongs to the domain should be 'exported' in external services, except things like SAGAs or complex interaction between root entities (not this case)
so, as I've written at the beginning of the answer:
why do you split the domain logic, implementing it within the several object involved in this changes?
It means that wo reads the code, months later, to understand when, how and why Status changes has to go through the changes of Minute, then to the handler, checking also its code (and I suppose has some logic that, if put inside Match, would require less 'hops'), and, finally, the code that really change Status.

Related

Updating a value object from the aggregate in one-to-many relationship

I have recently dived into DDD and this question started bothering me. For example, take a look at the scenario mentioned in the following article:
Let's say that a user made a mistake while adding an EstimationLogEntry to the Task aggregate, and now wants to correct that mistake. What would be the correct way of doing this? Value objects by nature don't have identifiers, they are identified by their structure. If this was a Web application, we would have to send the whole EstimationLogEntry value object as a request parameter, along with the new values, just so we could replace the old value object with the new one. Should EstimationLogEntry be an entity?
It really depends. If it's a sequence of estimations, which you append every time, you can quite possibly envision an operation which updates the value only of the VO. This would use VO semantics (the VO is called to clone itself in-mem with the updated value on the specific property), and the command can just be the estimation (along with a Task id).
If you have an array of VO's which all semantically apply to Task (instead of just the "latest" or something)... it's a different matter. In that case, you'd probably have to send all of them in the request, and you'd have to include all properties too, but I'd say that the need to change just one, probably implies a need to reference them, which in turn implies a need to have an Entity instead of a VO.
DDD emphasizes the Ubiquitous language and many modelling questions like this ones will derive their answer straight from that language.
First things first, if there's an aggregate that contains a value object, there's a good chance that the value object isn't directly created by the user. That is, the factory that creates the value object lives on the aggregates API. The value object(s) might even be derived directly from the aggregates state instead of from any direct method call. In this case, do you want to just discard the aggregate and create a new one? That might make sense depending on your UL.
In some cases, like if you have immutable value objects (based on your UL), you could simply add a new entry into the log entry that "reverses" the old entry. An example of this would be bank accounts and transactions. If bank accounts are aggregate roots and transactions are the value objects. If a transaction is erroneously entered, you can simply write a reversing transaction to void it.
It is definitely possible that you want to update the value object but that must make sense in your UL and it's implementation must also be framed around your UL. For example, if you have a scheduling application and an aggregate root is a person's schedule while the value objects are meetings. If a user erroneously enters a meeting, what your aggregate root should do would be to invalidate the old meeting (flip a flag, mark its state cancelled e.t.c) and create a new one. These actions fit the UL for your scheduling app. The same thing as what you are calling "updating the entry" above.

DDD => behaviour in root aggregate : instanciate other root aggregate

I have 2 root aggregate :
- invoice
- complaint
And I have a rule who say : "I can't delete an invoice if a complaint is opened on it".
On my delete behaviour on invoice aggregate I want to check if a complaint exist like :
Complaint complaint = ComplaintRepository.findByInvoiceId(invoiceId);
if(complaint.isOpened) {
throw new Exception("Open Complain...");
}
else{
...
}
My collegues and I are disagree on this.
They said to me that I can't instanciate a Complaint in my behaviour since Complaint is not in my Aggregate.
My opinion is that I can't have a Complaint attribute in Invoice Class, but :
- I can refered one with a Value Object (they are ok with this)
- I can read/load an instance since I did not call behaviour on it...
Do you have an opinion on this ?
Technically you can do what you're proposing: from a certain point of view, if you're injecting a ComplaintRepository interface into the invoice, either through constructor injection or method injection, you're making the Invoice dependant on the contracts of both the Repository and and the Complaint and that's pretty much allowed.
You are right when you say you can't hold a reference to the complaint, but you can inject DDD artifacts (such as factories/repositories/entities) into operations when they're needed to run.
However the main point you must ask yourself is: do you really want this level of coupling between two distinct aggregates? At this point, they're so coupled together they mostly can't operate without one and the other.
Considering all of this, you might be into the scenario where the complaint might just be a part of the invoice aggregate (although your invoice aggregate probably has other responsibilities and you will start to struggle with the "Design Small Aggregates" goal). If you think about it, that's what the invariant "I can't delete an invoice if a complaint is opened on it" is proposing.
If for all means it's not practical for you to model the complaint as part of the invoice aggregate, you have some other options:
Make these aggregates eventually consistent: instead of trying to delete the invoice in "one shot", mark it as flagged for deletion in one operation. This operation triggers some sort of Domain Event in you messaging mechanism. This event "InvoiceFlaggedForDeletion" will then check for complaints on the Invoice. If you have no complaints, you delete it. If you have complaints, you rollback the Deletion Flag.
Put the deletion process in a Domain Service. That way, the Domain Service will coordinate the efforts of checking for complaints and deleting the invoice when appropriate. The downside of this approach is that your Invoice entity will be less explicit about it's rules, but DDD-wise this sometimes is an acceptable approach.
This statement:
I have 2 root aggregate : - invoice - complaint`
and this
And I have a rule who say : "I can't delete an invoice if a complaint is opened on it"`
are mutually exclusive, if you follow the rule of not having a database transaction bigger than one Aggregate (and you should try to follow it, it's a good rule).
Aggregates are the transactional boundary, this means that what happens inside an Aggregate is strongly consistent with what it will happen in the same Aggregate in the future (the invariants will hold no matter what, the Aggregates are always in a valid state).
However, what happens between different Aggregate instances is eventually consistent, this means that nothing can prevent the system (of multiple Aggregates) to enter in an invalid state without a higher level coordination. Aggregates are responsible only for they data they own.
Code like yours:
Complaint complaint = ComplaintRepository.findByInvoiceId(invoiceId);
//
// at this time a new complain could be added!!!
//
if(complaint.isOpened) {
throw new Exception("Open Complain...");
}
else{
invoiceRepository.delete(invoiceId);// and this would delete the invoice although there is a complain on this invoice!!!
}
would fail to respect the business rule I can't delete an invoice if a complaint is opened on it, unless it is wrapped in a bigger-than-a-single-Aggregate transaction.
Having that said, you have two DDD-ish options:
Review your design: merge the two Aggregates into one, for example, make the Compliant a nested entity inside the Invoice.
Use a higher level coordinator that would model the "deletion" of an Invoice as a long running business process. For this you can use a Saga/Process manager. The "simplest" such a Saga would also delete the Complains that were added after the Invoice was deleted. A more complex Saga could even prevent the Complain to be added after the Invoice was deleted (for this it would need to somehow intercept the opening of a Complain).
Aggregate roots should not hold references to repository. This approach has number of issues. Instead, load all objects from repository in application service (command handler) and pass to domain for manipulation. If manipulation engulfs multiple aggregates, either the domain logic is wrong (missing concept) or you might need a domain service. Either way, aggregates are best kept out of asking repository anything.
Another consideration should be - what does delete an invoice mean in this domain?
See - http://udidahan.com/2009/09/01/dont-delete-just-dont/
In this case, if you challenge the domain experts, perhaps the requirement around 'deleting' invoices has just come from them being trained about databases and implicitly converting their real requirement into a solution for you to try and be helpful.
Perhaps what they are really talking about is cancelling an invoice? Or archiving it? Or reversing it?
In any case, all these would allow you to model a state transition on the invoice without having to worry about 'orphan' complaints.
This would then prompt the consideration - what should happen to complaints if an invoice is cancelled? Should the owner of the complaint be notified? Should the complaint undergo it's own state transition? This could be triggered by an InvoiceCancelled event.
In DDD, whenever you see requirements relating to deletion and concerns around orphan records, it's usually a hint that there is some deeper knowledge crunching to be done to understand the real intent of the domain.

Performing simple query inside of domain service

I've just run into a problem while trying to re-design our existing business objects to a adopt a more domain-driven design approach.
Currently, I have a Product Return aggregate root, which handles data related to a return for a specific product. As part of this aggregate, a date needs to be provided to say what month (and year) the return is currently for.
Each product return MUST be sequential, and so each product return must be the next month after the previous. Attempting to create a product return that doesn't follow this pattern should result in an exception.
I had thought about passing along a Domain Service to the method (or constructor) that sets the PeriodDate for the return, but I'm at a loss for how I would do this. Even if the domain service had a reference to a repository, I can't see it being appropriate to put a "GetNextReturnDate()" on that repository.
For background, each product return is associated with a product. I was reluctant to make the product the aggregate root, as loading up all the product returns just to add one seemed like an extremely non-performant way of doing things (considering this library is going to be used with a RESTful Web API).
Can anyone provide suggestions as to how I should model this? Is it a matter of just changing the aggregate root and dealing with the performance? Is there some place in the Domain that 'query' type services can be placed?
As an example, the current constructor the for product return looks like this:
public ProductReturn(int productID, int estimateTypeID, IProductService productService)
{
// This doesn't feel right, and I'm not sure how to implement it...
_periodDate = productService.GetNextReturnDate(productID);
// Other initialization code here...
}
The IProductService (and it's implementation) sit in the Domain layer, so there's no ability to call SQL directly from there (and I feel that's not what I should be doing here anyway)
Again, in all likelihood I've modelled this terribly, or I've missed something when designing the aggregate, so any help would be appreciated!
I Think my broader problem here is understanding how to implement constraints (be it foreign, unique, etc.) inside of a domain entity, without fetching an entire list of returns via a domain service, when a simple SQL query would give me the information required
EDIT: I did see this answer to another question: https://stackoverflow.com/a/48202644/9303178, which suggests having 'Domain Query' interfaces in the domain, which sound like they could return the sort of data I'm looking for.
However, I'm still worried I'm missing something here with my design, so again, I'm all open to suggestions.
EDIT 2: In response to VoiceOfUnreason's answer below, I figured I'd clarify a few things RE the PeriodDate property.
The rules on it are as follows:
CANNOT be NULL
It MUST be in sequence with other product returns, and it cannot be in a valid state without this fulfilled
It's a tricky one. I can't rely on the date passed in because it could then very well be out of sequence, but I can't figure out the date without the service being injected. I am going to transform the constructor into a method on a Factory to remove the 'constructor doing work' anti pattern.
I may be overly defensive over the way the code is currently, but it feels the amount of times the ReturnService has to be injected is wrong. Mind you, there are lots of scenarios where the return value must be recalculated, but it feels as though it would be easy to do this just before a save (but I couldn't think of a clean way to do this).
Overall I just feel this class has a bit of a smell to it (with the injected services and whatnot), but I may be needlessly worrying.
I had thought about passing along a Domain Service to the method (or constructor) that sets the PeriodDate for the return, but I'm at a loss for how I would do this.
I strongly suspect that passing a domain service to the method is the right approach to take.
A way of thinking about this: fundamentally, the aggregate root is a bag of cached data, and methods for changing the contents of the bag of data. During any given function call, it's entire knowledge of the world is that bag, plus the arguments that have been passed to the method.
So if you want to tell the aggregate something that it doesn't already know -- data that isn't currently in the bag -- then you have to pass that data as an argument.
That in turn comes in two forms; if you can know without looking in the aggregate bag what data to pass in, you just pass it as an argument. If you need some of the information hidden in the aggregate bag, then you pass a domain service, and let the aggregate (which has access to the contents of the bag), pass in the necessary data.
public ProductReturn(int productID, int estimateTypeID, IProductService productService)
{
// This doesn't feel right, and I'm not sure how to implement it...
_periodDate = productService.GetNextReturnDate(productID);
// Other initialization code here...
}
This spelling is a little bit odd; constructor does work is usually an anti-pattern, and it's a bit weird to pass in a service to compute a value, when you could just compute the value and pass it in.
If you feel like it's part of the business logic to decide how to compute _periodDate, which is to say if you think the rules for choosing the periodDate belong to the ProductReturn, then you would normally use a method on the object to encapsulate those rules. On the other hand, if periodDate is really decided outside of this aggregate (like productID is, in your example), then just pass in the right answer.
One idea that may be crossing you up: time is not something that exists in the aggregate bag. Time is an input; if the business rules need to know the current time to do some work, then you will pass that time to the aggregate as an argument (again, either as data, or as a domain service).
The user can’t pass a date in, because at any given time, the date for a return can ONLY be the next date from the last return.
Typically, you have a layer sitting between the user and the domain model -- the application; it's the application that decides what arguments to pass to the domain model. For instance, it would normally be the application that is passing the "current time" to the domain model.
On the other hand, if the "date of the last return" is something owned by the domain model, then it probably makes more sense to pass a domain service along.
I should also mention - a return is invalid without a date, so I can’t construct the entity, then hope that the method is called at a later time
Are you sure? Effectively, you are introducing an ordering constraint on the domain model - none of these messages is permitted unless that one has been received first, which means you've got a race condition. See Udi Dahan's Race Conditions Don't Exist
More generally, an entity is valid or in valid based on whether or not it is going to be able to satisfy the post conditions of its methods, you can loosen the constraints during construction if the post conditions are broader.
Scott Wlaschin's Domain Modeling Made Functional describes this in detail; in summary, _periodDate might be an Or Type, and interacting with it has explicit choices: do this thing if valid, do this other thing if invalid.
The idea that constructing a ProductReturn requires a valid _periodDate isn't wrong; but there are tradeoffs to consider that will vary depending on the context you are operating in.
Finally, if any date is saved to the database that ISN’T the next sequential date, the calculation of subsequent returns will fail as we require a sequence to do the calculation properly.
If you have strong constraints between data stored here and data stored somewhere else, then this could indicate a modeling problem. Make sure you understand the implications of Set Validation before you get too deeply invested into a design.
Your problem is querying across multiple aggregates(product returns) for decision making(creating a new product return aggregate).
Decision making based on querying across aggregates using a repository will always be wrong; we will never be able to guarantee consistency, as the state read from the repository will always be a little old.(Aggregates are transaction boundaries. The state read from repository will only be correct at that very instant. In the next instant the aggregate's state may change.)
In your domain what I will do is create a ProductReturnManager AggregateRoot which manages returns for a particular product and a ProductReturn Aggregate which specify one specific return of the product. ProductReturnManager AggregateRoot manages the lifecycle of ProductReturnAggregate to ensure consistency.
The logic for assigning a next month sequential date to ProductReturn is in ProductReturnManager (basically ProductReturnManager act as a constructor). The behavior of product return will be in ProductReturnAggregate.
The ProductReturnManager can be modeled as a Saga, which is created on first CreateProductReturnCommand (for a productId), and same saga is loaded for further CreateProductReturn commands(correlated by productId). It handles ProductReturnCreatedEvent to update its state. Saga creation logic will be according to your business rules(Eg. saga creation is done on InvoiceRaisedForProduct event and handles CreateProductReturn commands.)
sample code:
ProductReturnManagerSagaState{
ProductId productId;
//can cache details about last product return
ProductReturnDetails lastProductReturnDetails;
}
ProductReturnManagerSaga : Saga<ProductReturnManagerSagaState>,IAmStartedByMessages<CreateProductReturn>{
Handle(CreateProductReturn message){
//calculate next product return date
Date productReturnDate = getNextReturnDate(Data.lastProductReturnDetails.productReturnDate);
//create product return
ProductReturnAggregateService.createProductReturn(Data.productId, productReturnDate);
}
Handle(ProductReturnCreatedEvent message){
//logic for updating last product return details in saga state
}
}
ProductReturnAggregate{
ProductId productId;
Date productReturnDate;
ProductPayment productPayment;
ProductReturnState productReturnState;
//commands for product return
markProductReturnAsProcessing();
}
This is an excellent video by Udi Dahan on working across multiple aggregates.

Check command for validity with data from other aggregate

I am currently working on my first bigger DDD application. For now it works pretty well, but we are stuck with an issue since the early days that I cannot stop thinking about:
In some of our aggreagtes we keep references to another aggregate-root that is pretty essential for the whole application (based on their IDs, so there are no hard references - also the deletion is based on events/eventual consistency). Now when we create a new Entity "Entity1" we send a new CreateEntity1Command that contains the ID of the referenced aggregate-root.
Now how can I check if this referenced ID is a valid one? Right now we check it by reading from the other aggregate (without modifying anything there) - but this approach somehow feels dirty. I would like to just "trust" the commands, because the ID cannot be entered manually but must be selected. The problem is, that our application is a web-application and it is not really safe to trust the user input you get there (even though it is not accessibly by the public).
Did I overlook any possible solutions for this problems or should I just ignore the feeling that there needs to be a better solution?
Verifying that another referenced Aggregate exists is not the responsibility of an Aggregate. It would break the Single responsibility principle. When the CreateEntity1Command arrive at the Aggregate, it should be considered that the other referenced Aggregate is in a valid state, i.e. it exists.
Being outside the Aggregate's boundary, this checking is eventually consistent. This means that, even if it initially passes, it could become invalid after that (i.e. it is deleted, unpublished or any other invalid domain state). You need to ensure that:
the command is rejected, if the referenced Aggregate does not yet exists. You do this checking in the Application service that is responsible for the Use case, before dispatching the command to the Aggregate, using a Domain service.
if the referenced Aggregate enters an invalid state afterwards, the corrects actions are taken. You should do this inside a Saga/Process manager. If CQRS is used, you subscribe to the relevant events; if not, you use a cron. What is the correct action it depends on your domain but the main idea is that it should be modeled as a process.
So, long story short, the responsibilty of an Aggregate does not extend beyond its consistency boundary.
P.S. resist the temptation to inject services (Domain or not) into Aggregates (throught constructor or method arguments).
Direct Aggregate-to-Aggregate interaction is an anti-pattern in DDD. An aggregate A should not directly send a command or query to an aggregate B. Aggregates are strict consistency boundaries.
I can think of 2 solutions to your problem: Let's say you have 2 aggregate roots (AR) - A and B. Each AR has got a bunch of command handlers where each command raises 1 or more events. Your command handler in A depends on some data in B.
You can subscribe to the events raised by B and maintain the state of B in A. You can subscribe only to the events which dictate the validity.
You can have a completely independent service (S) coordinating between A and B. Instead of directly sending your request to A, send your request to S which would be responsible for a query from B (to check for validity of referenced ID) and then forward request to A. This is sometimes called a Process Manager (PM).
For Example in your case when you are creating a new Entity "Entity1", send this request to a PM whose job would be to validate if the data in your request is valid and then route your request to the aggregate responsible for creating "Entity1". Send a new CreateEntity1Command that contains the ID of the referenced aggregate-root to this PM which uses ID of the referenced AR to make sure it's valid and if it's valid then only it would pass your request forward.
Useful Links: http://microservices.io/patterns/data/saga.html
Did I overlook any possible solutions for this problems
You did. "Domain Services" give you a possible loop hole to play in.
Aggregates are consistency boundaries; their behaviors are constrained by
The current state of the aggregate
The arguments that they are passed.
If an aggregate needs to interact with something outside of its boundary, then you pass to the aggregate root a domain service to encapsulate that interaction. The aggregate, at its own discretion, can invoke methods provided by the domain service to achieve work.
Often, the domain service is just a wrapper around an application or infrastructure service. For instance, if the aggregate needed to know if some external data were available, then you could pass in a domain service that would support that query, checking against some cache of data.
But - here's the trick: you need to stay aware of the fact that data from outside of the aggregate boundary is necessarily stale. There might be another process changing the data even as you are querying a stale copy.
The problem is, that our application is a web-application and it is not really safe to trust the user input you get there (even though it is not accessibly by the public).
That's true, but it's not typically a domain problem. For instance, we might specify that an endpoint in our API requires a JSON representation of some command message -- but that doesn't mean that the domain model is responsible for taking a raw byte array and creating a DOM for it. The application layer would have that responsibility; the aggregate's responsibility is the domain concerns.
It can take some careful thinking to distinguish where the boundary between the different concerns is. Is this sequence of bytes a valid identifier for an aggregate? is clearly an application concerns. Is the other aggregate in a state that permits some behavior? is clearly a domain concern. Does the aggregate exist at all...? could go either way.

Save/Validate Entity

I'm a novice with domain driven design and learning to apply it in my current project. I hope some of you guys have already walked the path and can help me out.
I have a question with regard to saving UI changes back to an Entity (Order).
The scenario:
a. An approver opens the Order (Aggregate root) pending approval on the Web. Makes some changes and clicks the button "Approve".
b. The UI translates the Order changes to a DTO and posts it across to a Web service for processing.
c. The service pulls the Order from OrderRepository via say call to orderRep.GetByID(ApplicationNumber)
Question
1. How do I post the UI changes available in the OrderDTO to Order?
2. What are the different things I need to take care while hydrating the Order?
(If we have to ensure that the domain object (Order) doesn't land up in
invalid state due to changes)
Each user operation should correspond to a different command method in the application service layer. Much of the time it will correspond to exactly one call on a domain object.
You probably don't have fine-grained enough methods on your Order domain object.
Approve() should probably only be a method, not a public setter. Throw an exception within Approve() if it would place the Order object in an invalid state.

Resources