I have an Invoice aggregate root which at some point can be sent to accounting external web service, and mark as sent by persisting some ID/number obtained from that service.
Which is the correct way to do it in DDD?
Here are my ideas:
First apprroach:
Have an invoice AggregateRoot with function SendToAccounting, and inject domain service / interface, which will send invoice to accounting, and retrieve some "id/code" in the accounting software, and set AccountingSoftwareId property
Invoice.SendToAccounting(IInvoiceDomain service)
{
var accountingSoftwareID = service.getAccountingSoftwareId(this);
this.AccountingSoftwareId = accountingSoftwareId;
}
///Implementation in the application service
var invoice = _invoiceRepository.GetInvoiceById(id);
invoice.SendToAccounting(someDomainService);
_invoiceRepository.Update(invoice);
_unitOfWork.Save();
Second approach:
Similar as first approach, but domain service should be responsible for persisting like this:
var invoice = _invoiceRepository.GetInvoiceById(id);
///unit of work save will be called inside this function
invoice.SendToAccounting(someDomainService);
Third approcach:
Domain service will be fully rensponsible to encapsulate this behavior
///Code inside domain service
public void SendInvoiceToAccounting(int invoiceId)
{
var invoice = _invoiceRepository.GetInvoiceById(invoiceId);
string invoiceAccountingId = _accountingService.GetAccountingSoftwareId(invoice);
invoice.SetAsSentToAccounting(invoiceAccountingId);
_invoiceRepository.Update(invoice);
_unitOfWork.Save();
}
Which is the correct way to do it in DDD?
Your first approach is closest. The signature on your domain service should accept state as arguments, rather than the aggregate root itself.
Invoice.SendToAccounting(IInvoiceDomain service)
{
var accountingSoftwareID = service.getAccountingSoftwareId(this.Id, ...);
this.AccountingSoftwareId = accountingSoftwareId;
}
All of the arguments passed should be value types - the domain service shouldn't be able to change the state of the aggregate by manipulating its copy of the arguments, and it certainly shouldn't be able to run other commands on the aggregate.
In a code review, I would reject the second approach you offer; from the point of view of the domain model, the domain service interface should only provide queries, not commands (in the CQS sense).
In a code review, I would reject the third approach completely -- setters on aggregates are a code smell; the whole point is to encapsulate the state with the rules for updating it.
BUT
The design is somewhat alarming, in that you are making writes in two different places in the same transaction. In the happy path, it isn't a big deal, but what are you supposed to do if the command run on the accounting service succeeds, but the save of the updated invoice fails?
Assuming that distributed transactions aren't appealing, you may want to review what Udi Dahan has to say about reliable messaging.
My first thought was 'isn't invoicing part of accounting?' :)
option 1 is what I've tended to use in the past where my domain objects have behaviour.
I dont like option 2 as then Invoice needs a private reference to the repository.
A more general observation is that there doesn't seem to be much behaviour in the domain here - it seems to just be setting an id. Option 3 seems to capture this. I'm wondering if an application service would suffice and just coordinate the following
Load the invoice
Get an AccountingId
Save it on the invoice
Which is pretty much option 3 above. I'd be tempted to pass in the repository and service though, but thats really just a more functional style - the above would work too with private fields.
The accounting BC should always return the same accountingSoftwareId for a given invoiceId.
If in the first round, the call is made on the accounting BC but the invoice update fails, you have a state t1 in the account BC and a state t0 in the invoicing BC. When you retry the command, it will perform the same call and return the same id, and if the update is successful you are in t1 state in each BC. In the worst case, even if the command has to be resolved manually, the resulting account id would always be the same for a given invoice id.
So to resolve an accounting ID for a particular invoice, you could ask the accounting BC directly.
Related
I will try to keep this as generic as possible using the “order” and “product” example, to try and help others that come across this question.
The Structure:
In the application we have 3 different services, 2 services that follow the event sourcing pattern and one that is designed for read only having the separation between our read and write views:
- Order service (write)
- Product service (write)
- Order details service (Read)
The Background:
We are currently storing the relationship between the order and product in only one of the write services, for example within order we have a property called ‘productItems’ which contains a list of the aggregate Ids from Product for the products that have been added to the order. Each product added to an order is emitted onto Kafka where the read service will update the view and form the relationships between the data.
The Problem:
As we pull back by aggregate Id for the order and the product to update them, if a product was to be deleted, there is no way to disassociate the product from the order on the write side.
This in turn means we have inconsistency, that the order holds a reference to a product that no longer exists within the product service.
The Ideas:
Master the relationship on both sides, which means when the product is deleted, we can look at the associated orders and trigger an update to remove from each order (this would cause duplication of reference).
Create another view of the data that shows the relationships and use a saga to do a clean-up. When a delete is triggered, it will look up the view database, see the relationships within the data and then trigger an update for each of the orders that have the product associated.
Does it really matter having the inconsistencies if the Product details service shows the correct information? Because the view database will consume the product deleted event, it will be able to safely remove the relationship that means clients will be able to get the correct view of the data even if the write models appear inconsistent. Based on the order of the events, the state will always appear correct in the read view.
Another thought: as the aggregate Id is deleted, it should never be reused which means when we have checks on the aggregate such as: “is this product in the order already?” will never trigger as the aggregate Id will never be repurposed meaning the inconsistency should not cause an issue when running commands in the future.
Sorry for the long read, but these are all the ideas we have thought of so far, and I am keen to gain some insight from the community, to make sure we are on the right track or if there is another approach to consider.
Thank you in advance for your help.
Event sourcing suites very well human and specifically human-paced processes. It helps a lot to imagine that every event in an event-sourced system is delivered by some clerk printed on a sheet of paper. Than it will be much easier to figure out the suitable solution.
What's the purpose of an order? So that your back-office personnel would secure the necessary units at a warehouse, then customer would do a payment and you start shipping process.
So, I guess, after an order is placed, some back-office system can process it and confirm that it can be taken into work and invoicing. Or it can return the order with remarks that this and that line are no longer available, so that a customer could agree to the reduced order or pick other options.
Another option is, since the probability of a customer ordering a discontinued item is low, just not do this check. But if at the shipping it still occurs - then issue a refund and some coupon for inconvenience. Why is it low? Because the goods are added from an online catalogue, which reflects the current state. The availability check can be done on the 'Submit' button click. So, an inconsistency may occur if an item is discontinued the same minute (or second) the order has been submitted. And usually the actual decision to discontinue is made up well before the information was updated in the Product service due to some external reasons.
Hence, I suggest to use eventual consistency. Since an event-sourced entity should only be responsible for its own consistency and not try to fulfil someone else's responsibility.
I take the example of StackOverflow because obviously you know that website, and my real usecase is really close.
So let's imagine a simplified SO domain description:
There are users
Users can create new questions
Users can create answers to these questions
Users can edit their own questions and answers
Users can edit other users questions if they have more than 1000 reputation (took that threshold randomly)
The last bold rule is the one that matters to me.
What I understand about an AggregateRoot is that it should contain the state that serves to take decision to accept or reject commands, and it should not query a DB to do so. It guarantees the consistency of the app. It should only listen to the events it emits to update its state.
Now what I think is that the SO domain has an aggregate root called Question.
That question would then handle commands like:
CreateAnswer
EditQuestion
The thing is, when the EditQuestion is fired, how the Question AggregateRoot would be able to decide wether to accept or refuse that command? Because if you remember, the command should be rejected if you try to edit the question of another user if you have < 1000 reputation.
It does not seem to make sens to me that the Question AR maintain a list of all users reputations, to be able to know how to act on that command.
The problem is that when trying to model my domain I have this modeling problem coming over and over again, and I always end up with a single big fat AggregateRoot
Can someone tell me what I am missing and help me solve this problem? thanks
This question seems to say that we should not put the authorization system inside the domain model. I agree this may be practical for things like role-based authentication. However, to me the "users can't edit unless they have enough reputation" is really an SO business rule, so how could it be outside of the domain?
IMPORTANT: when answering, please consider YOU are the business expert. You know StackOverflow as an user and can guess what are the SO constraints by yourself. Even if you are wrong about them, it's not a big deal: just make a proposal for your wrong business constraints I'm fine with that!!!
It's not the first time I ask this kind of question and it always ends up with no answer but just endless discussions. What I want to know is how you would model StackOverflow if you had to build this site, with a focus on the business rules about the minimum reputation to edit.
Well, things are quite simple IMO (in this SO scenario only). This is how I would do it (obviously, other devs probably have different approaches):
You tagged the question well with the "cqrs". In the EditQuestion handler, I'd use a Domain Service (a Query from a CQRS point of view) which will check if a certain use has the required points and then return a true/false. Something like this (more or less pseudo code)
public class CanUserEditQuestionService
{
//constructor with deps\\
public bool Handle(CanUserEditQuestion input)
{
//query the read model, maybe a query object to get us the rep of the user
var rep=getReputation.Get(input.UserId);
//we can have a dependency here which tell us the number of points required for a specific permission
return(rep>=1000);
}
}
If the query returns true, then the handler will perform changes on the Question entity i.e question.ChangeText() or smth (I think SO takes an event sourcing approach).
What you have here is a simple use case of a concept "Question" , its command behaviour "Edit" and the business rule that dictates who can do what. The thing is, the 1000 rep rule is never part of the Question concept definition, therefore it doesn't belong to that aggregate i.e how the question is edited. However it's part of the use case itself and part of the application service.
I'm sure that you'll ask me : "What if the read model used by the domain query is behind the command model?". In this case it matters very little, the delay it's probably measured in seconds at most. Also the main thing here is: the business rule is not part of the Question aggregate so it doesn't care about being immediate consistent.
Another thing is that user rep is always a different concept than of a Question so dealing with rep should be never a part of a Question aggregate. But it is part of the application service.
If you view an application as a group of use cases doing things with concepts (which themselves encapsulate data and business constraints), it's quite easy to identify which is the application service, aggregate, domain service etc.
A slightly different approach if you want immediate consistency
public class UserCommandHandler
{
public void Handle(EditQuestion command)
{
// (start transaction)
var user = userRepository.Get(command.UserId);
if (user.Reputation < 1000)
// reject command here
var edit = user.EditedQuestion(...); // or just "new QuestionEdit(...)"
questionEditRepository.Add(edit);
// (commit, saving the QuestionEdited event)
}
}
Since we're using CQRS, the state of a Question as reflected on a page would not be contained in an Question Aggregate but a projection of a series of QuestionEdited events that were listened to and cumulated over time.
I'm new to the CQRS/ES world and I have a question. I'm working on an invoicing web application which uses event sourcing and CQRS.
My question is this - to my understanding, a new command coming into the system (let's say ChangeLineItemPrice) should pass through the domain model so it can be validated as a legal command (for example, to check if this line item actually exists, the price doesn't violate any business rules, etc). If all goes well (the command is not rejected) - then the appropriate event is created and stored (for example LineItemPriceChanged)
The thing I didn't quite get is how do I keep this aggregate in memory to begin with, before trying to apply the command. If I have a million invoices in the system, should I playback the whole history every time I want to apply a command? Do I always save the event without any validations and do the validations when constructing the view models / projections?
If I misunderstood any part of the process I would appreciate your feedback.
Thanks for your help!
You are not alone, this is a common misunderstanding. Let me answer the validation part first:
There are 2 types of validation which take place in this kind of system. The first is the kind where you look for valid email addresses, numeric only or required fields. This type is done before the command is even issued. A command which contains these sorts of problems should not be raised as commands (for belt and braces you can check at the domain side but this is not a domain concern and you are better off just preventing this scenario).
The next type of validation is when it is a domain concern. It could be the kind of thing you mention where you check prices are within a set of specified parameters. This is a domain concept the business people would understand, do and be able to articulate.
The next phase is for the domain to apply the state change and raise the associated events. These are then persisted and on success, published for the rest of the app.
All of this is can be done with the aggregate in memory. The actions are coordinated with a domain service which handles the command. It loads the aggregate, apply's all it's past events (or loads a snapshot) then issues the command. On success of the command it requests all the new uncommitted events and tries to persist them. On success it publishes the new events.
As you see it only loads the events for that specific aggregate. Even with a lot of events this process is lightning fast. If performance is a problem there are strategies such as keeping aggregates in memory or snapshotting which you can apply.
To your last point about validating events. As they can only be generated by your aggregate they are trustworthy.
If you want more detail check out my overview of CQRS and ES here. And take a look at my post about how to build aggregate roots here.
Good luck - I hope they help!
It is right that you have to replay the event to 'rehydrate' the domain aggregate. But you don't have to replay all events for all invoices. If you store the entity id of the root aggregate in the events, you can just select and replay the events that with the relevant id.
Then, how do you find the relevant aggregate root id? One of the read repositories should contain the relevant information to get the id, based on a set of search criteria.
I'm using CQRS on an air booking application. one use case is help customer cancel their tickets. But before the acutal cancellation, the customer wants to know the penalty.
The penalty is calculated based on air rules. Some of our provider could calculate the penalty through exposing an web service while the others don't. (They publish some paper explaining the algorithm instead). So I define a domain service
public interface AirTicketService {
//ticket demand method
MonetaryAmount penalty(String ticketNumber);
void cancel(String ticketNumber, MonetaryAmount penalty);
}
My question is which side(command/query) is responsible for invoking this domain service and returning result in a CQRS style application?
I want to use a Command: CalculatePenlatyCommand, In this way, it's easy to resuse the domain model, but it's a little odd because this command does not modify state.
Or should I retrieve a readmodel of ticket if this is a query? But the same DomainService is needed on both command and query side, it's odd too.
Is domain derivation a query?
There is no need to shoehorn everything in to the command-query pipeline. You could query this service independently from the UI without issuing a command or asking the read-model.
There is nothing wrong with satisfying a query using an existing model if it "fits" both the terminology and the structure of that model. No need to build up a separate read model for that purpose. It's not without risk, since the semantics and the context of the query should be closely tied to the model that is otherwise used for write purposes only. The risk I allude to is the fact that the write and read concerns could drift apart (and we're back at square one, i.e. the reason why people pick CQRS in the first place). So you must keep paying attention as new requirements come in.
Queries that fit this model really well are what I call "simulators', where you want to run a simulation using current state to e.g. to give feedback to an end user. On more than one occasion I've found that the simulation logic could be reused both as a feedback mechanism and as an execution (of a write operation/command) steering mechanism. The difference is in what we do with the outcome of the simulation. Again, this is not without risk and requires careful judgement.
You may bring arguments that Calculate Penalty Command is not odd at all.
The user asks the system to do something - command enough.
You can even have a Penalty Calculation Requested Event event in your domain, and it would feel right. Because, at some time, you may be interested in, let's say, unsure clients, ones that want to cancel tickets but they change their mind every time etc. The calculation may be performed asynchronously, too - you can provide the result (penalty cost) to the user in various ways afterwards...
Or, in some other way: on your ticket booked event, store cancellation penalty, too. Then, you can make that value accessible any time, without the need to recompute it... But this may be wrong (?) because penalty would largely depend on time, right (the late you cancel your ticket, the more you pay)?
If all this would like over-complications etc., then I guess I agree with rmac's answer, too :)
I have following problem.
Given. CQRS + EventSourcing application.
How is that possible to change the state of the Aggregate root in history?
For example, accounting application, accounter wants to apply transation but with past date. The event which will be stored in Event Store will have the older date than recent events, but the sequense number of this event will be bigger.
Repository will restore the state of aggregate root by ordering events by sequence number. And if we will take the snapshot for this past date - we will have aggregate root without this event.
We can surely change the logic of repository to order events by date, but we use external framework for CQRS, and this is not desirable.
Are there some elegant solutions for this case?
What you're looking for is a bi-temporal implementation.
e.g. On dec 3rd we thought X == 12 (as-at), but on dec 5th we corrected the mistake and now know X == 14 on dec 3rd (as-of)
There are two ways to implement this
1) The event store holds as-at data and a projection holds as-of data (a possible variation is both an as-of and as-at projection as well)
2) The aggregate has an overloaded method indicating the desire for as-of vs as-at values from the event store. This will most likely involve using a custom secondary snapshot stream for as-of data values.
Your solution could very likely use both implementations as one is command focused and the other is query focused.
The as-of snapshots for the aggregate root in the second option would need to be rebuilt as corrective events are recieved.
Martin Folwler talks about this in this article
Note: The event store is still append only.
In accounting you'll probably end up in jail if you change past bookings. Don't change the past. Use compensating commands instead.
Sorry, but you brought up the accounting example, which is probably a domain that's very strict about fiddling with past data without making the changes explicit.
If the above doesn't apply to your domain you can easily apply new events on top of older ones that change the state (and possibly the history) of your domain objects.
Take a booking to an account for example. The event might have occurred today, but it can set the actual booking date to some time in the past.
You have stated that your business logic allows you to add a back-dated transaction; now I don't know why you'd want that, but there's nothing constraining your aggregate not to accept it. Of course the event will get a later event sequence number/version, but that's expected.
You don't need to fiddle with the infrastructure, repository or anything else to do this.
Accounting doesn't let you change history. It only lets you add entries. It's up to your business logic to interpret the dates on these events as you will. In this case, the sequence of events is not just a persistence trick as with event sourcing, but the actual content of the domain!
One solution to this is to think of the event as an explicit compensating action. For example, when your bank reverses a charge, they don't delete an existing transaction, they add a compensating transaction. This transaction may reference they transaction it wishes to compensate with respective dating. In this way, the events are a proper representation of reality.