[Axon 2.4.6] Aggregates and business processes

Hi all,

I’m having an hard time with one use case I have in my application.

As an example speaks better than words :

I’m dealing with warehouse items that are stored in a warehouse. Ultimately, these warehouse items will be put in auction.

One requirement is that each warehouse item has to be evaluated ( price + grade) beforehand.

The evaluation process itself has some rules :

  1. the item has to be evaluated at least by 2 diffferent staffs
  2. the difference between 2 evaluation price must be within a specifc range
  3. the final price is set by a manager
  4. The 2 staffs can edit their evaluations as much as they want as soon as the final price is not confirmed by the manager.
  5. The item can be reevaluated later on but it’s a new instance of the evaluation process, obeying the same rules.

So far, so good, right now I already have WarehouseItem as an AR.

As these temporary evaluations are not part of the warehouse item state for me (only the final price and grade matter), I don’t feel comfortable to store them into it and to handle the process through the WarehouseItem AR.

My first idea was the following :

  • Create a Evaluation Aggregate Root that references the Warehouse item aggregate ( created directly via a command or by the following saga upon the first evaluation). This way, I can edit each evaluation (rule #4)
  • Use a Saga as the evaluation process tracker, handling the transitions and the associated business rules
    1. Set the final price and grade by sending a command from the Saga whenever the manager confirms the price 2) Mark the Evaluations as Read-only

The problem I see in that case is that I have to reach the Saga before knowing whether the Evaluation is valid or not, as it’s the only place where we have a reference to all evaluations for a particular warehouse item (to check rule #2)
It means I have to adjust the state of the evaluation afterwards (mark it as invalid, and update the read model accordingly). This is not as convenient and efficient as rejecting the command creating the evaluation at the first place.

I’m starting to think that it might be better to have an AR EvaluationRound for example, that centralizes all the evaluations for the specific item.
This way, when the EvaluationRound is completed, I would use a stateless event handler to update the warehouse item final price and grade.

Am I on the right track, or would you have any suggestions, best practices to handle this kind of (common) case ?

Thank you in advance,

Hi Jerome,

a first think to keep in mind is that the boundaries are primarily driven by consistency guarantees, not so much our feeling of “what belongs together”. That said, I also know that gut feel is an important indicator that something is wrong. Just make sure to keep all options open. Sometimes, it just feels weird because we consider the query part of these two concepts different. Don’t let that be a factor in designing the command model.

I do agree that the concept of Evaluation is something to separate from the concept of a Warehouse item. However, an option is also to consider these two concepts as Entities within the same Aggregate. In terms of consistency, that would help, as the aggregate is always consistent.
In either case, the Evaluation entity is responsible for handling temporary evaluations, detecting too large discrepancies, accepting final evaluations, etc.

When an Evaluation has become final, the difference between “entity within the same aggregate” and “separate aggregate” becomes more obvious. In the first case, the event indicating the final Evaluation is directly available to the WarehouseItem entity (which may, or may not be the aggregate root). So the Warehouse Item can take over this evaluation value (if necessary). If they are in separate aggregate, the event published by the Evaluation aggregate will need to be handled by the WarehouseItem aggregate. If possible, always choose a stateless event handler over a Saga.

As far as I know, I haven’t seen substantial “evidence” in the description above that would warrant separation of these two entities in different Aggregates. But neither have I seen the opposite ;-). Both options are doable. The single aggregate solution is less chatty, though.

Hope this helps.
Cheers,

Allard

Hi Allard,

Thank you for your answer, it surely helps.

I think the main reason I wanted to make Evaluation a separate AR is that they have a different lifecycle, but maybe it’s a bad reason. As I briefly explained, a warehouse item is subject to several evaluations rounds, each being independant of the other ( meaning we don’t take the result of the previous evaluation round into account).

Just to make things clear, an EvaluationRound is composed of :

  • at least two evaluation entries submitted by 2 staff
  • an average price
  • a status : STARTED or FINISHED

When the manager confirms, the average price becomes the warehouse item final price ( until the next round).

If I model Evaluation as an entity part of the same AR, it means that every time we start a new round, I have to :

  • clear the current evaluation price
  • reset the Evaluation state

So basically replacing the Evaluation object.
So it’s not clear in my mind if Evaluation should instead be kind of VO, as the staff can edit the nested Evaluation entries (but as one staff can only have one evaluation entry per warehouse item, editing means replacing the existing one with the corrected values).

As you can see, it’s still messy in my mind, I may have to try both implementations to figure out which one is the most consistent and aligned as much as possible with the DDD principles.

Regards,
Jerome