Maintaining consistency between aggregates

I am wondering how to solve problems of transaction consistency between aggregates. My first impression is that whenever you need transactional consistency between aggregates, you have designed your aggregates incorrectly. However, I would like to ask this question anyway to make sure I haven't missed anything.

Imagine you are selling cow's milk. You have several cows, each of which produces a certain amount of liters of milk per day. You need to have a service that is able to receive the quantity of milk in the warehouse. Alternatively, you can also order milk. On the basis of this information, you can create a three unit Cow

, Stock

andOrder

... Whenever a certain amount of milk is ordered, one of the business rules is to check if that amount is in stock, and if not, report it right away. How can this be achieved when two users make a parallel request and order a total of 150 liters of milk, while only 130 liters are available? My first idea is that you can achieve this with optimistic / pessimistic locking, but in this case one aggregate depends on the other. Is there a definite way to solve this particular problem, or is it just bad aggregate design?

+3


source to share


2 answers


My first impression is that whenever you need transactional consistency between aggregates, you have designed your aggregates incorrectly.

That's for sure; how you find aggregated boundaries is first determined by the values ​​that need to be consistent, and then you select boundaries that have the property that any two values ​​that need to be consistent with each other are on the same boundary.

Note: we don't always get it right; maybe the requirements were wrong, maybe our model was not adequate, maybe the business has changed. Part of the challenge is to make sure it's always easy to replace your current model with a better one.

Based on this information, you can create three aggregates: cow, stock, and order.

Note: Cow

- this is a lousy aggregate - if we are talking about the real world in the world eating cows cows. It is an entity, yes, but it is beyond the influence of the model. If the model says the cow is empty and the cow says it is full of milk, the cows are right.

Likewise, if the cows are real, then most of your supplies are real as well. If the model says there are seven full milk canisters and the farmer has six, then six is ​​the correct answer.



Aggregates are information resources.

Whenever a certain amount of milk is ordered, one of the business rules is to check if that amount is in stock, and if not, report it right away. How can this be achieved when two users make a parallel request and order a total of 150 liters of milk and only 130 liters are available?

Essential for awareness "right away"; you are here, the user (buyer?) is. There is a certain amount of delays in the message, which means that the information you are sending to the buyer is already out of date when it arrives. (Technically, it's already deprecated the moment you submit it.)

The second order fulfillment requires an understanding of both orders and available stock. So you can model it as a single aggregate that does everything, or you can model it as aggregates of orders that bind asynchronously to some execution aggregate; for example, it could be that what Stock actually does is confirm pending order milk availability notifications.

In the parallel processing example, it would look like two commands to reserve 130 liters of milk simultaneously working against the stock, with 150 liters of milk. Using optimistic concurrency, both teams will find that there is enough milk to saturate the order and will try to update the ledger. This update, however, is serialized - think of a transaction or compare and swap - so that one of the commands succeeds and the other gets a concurrent update exception instead. This second command then tries to reload the stock again in a new state. This time, he discovers that the available stock is insufficient and acts accordingly (moving the order to the waiting list, advising the buyer about the expected due date, etc.).

Note that you will also get concurrent modification exceptions when the command to reserve milk is run in parallel with the announcement that more milk is available.

+5


source


My first impression is that whenever you need transactional consistency between aggregates, you have designed your aggregates incorrectly.

I’ll go in the other direction and say: not necessary. You will always demand consistency between aggregates at a particular point in time. The only question is when. You may find that in some situations you are faced with some kind of policy. I had to implement 100% immediate consistency between a whole set of aggregates simply because I was tasked with doing it by those holding the wallet rows. In an ideal world, we would be free to choose. But in my real world, you can bet we will fail. In any case, the lyrics aside, of course, you can get away from the immediate sequence.

Changing more than one aggregate in a transaction is not the end of the world, but if you can avoid it, you definitely should. This does come with a small overhead that can be expected.



In your example, you can check the stock levels, but as noted, simultaneous readings lead to misleading information. However, you can place an order, and before starting your process, you can "reserve" the quantity using what will become your Process Manager ID and your full "Correlation ID". if you are unable to reserve stock, you can cancel the order. However, you will have to reserve multiple items for certain orders with more than one item.

There are many approaches to this (airline reservations can also be used as an example). You might want to take the order and see what you can do. At the same time, the order may have different turnaround times and more milk or other items. You can notify the customer that their order cannot be completed and canceled, or you can inform them to click a link to customize the order, or perhaps accept an alternative.

Just some thoughts :)

+3


source







All Articles