2

I'm currently working on this use case :

  1. a budget is assigned to an organization unit
  2. Mike orders a good using the budget
  3. Jane gives its OK to this order, validate it. This actions triggers theses consequences :
    1. check budget balance to see if we can accept this order : inc ase of failure, notify Jane.
    2. budget is reduced up to the order amount
    3. order status changed from WAITING_VALIDATION to VALIDATED
    4. A success message is displayed to Jane

Additional usecase informations :

  • for our users, the steps from 3.1 to 3.4 must be real-time. Jane clicks the button and waits for the answers : OK or NOT OK AND order status is VALIDATED

I'm thinking about :

  • budget is a bounded context.
  • ordering is another bounded context.
  • the validation step will change 2 aggregate roots which seems wrong. And add coupling. If we need to update another aggregate root at the same time (step 3, like creating an accounting depreciation entry for the good).
  • to be real-time, we can use an XA Transaction (SQL + Messaging for example) but I would like to avoid it (moreover my current technology stack doesn't allow me to use XA Transactions).

It seems that we can achieve this kind of usecase using eventual consistency : - Ordering service ask Budget service to handle a processOrder command (via messaging or REST) : at this point Ordering service is waiting for the Budget Service. - Budget Service handle the command (in a local transaction) and send OK / NOK to the caller - Budget service also send an OrderInBudgetProcessed event. - Ordering Service receives in realtime the OK / NOK and notify Jane (but the order status is not changed at this time) - Ordering Service handle the OrderInBudgetProcessed and update the order status in a local transaction.

I think this can works. However I have some issues :

  • during the time where the order status is not updated, Jane can't print the order to send it to the supplier.
  • during the time where the order status is not updated, we can imagine that Mike want to cancel the order. The status is still WAITING so the action "Cancel" is allowed. Must we ask the BudgetService that the order was already processed ? If yes, so we must ask all the services using orders their state. We can use something like a saga or coordinator ?
  • putting compensation actions seems a lot of work ...

Some questions :

  • The "order validation" - composed of 2 steps - is primarily handled by the budget context or the order context ? (seems to me that the main event is the budget diminution so I was thinking about the budget context). Seems logic to you ?
  • what do you think of this kind of usecase ? too complex ?
  • how do you handle this ?
  • what do I miss to have at the same time a clean code (avoiding a service that update / synchronize all the objets in the same transaction) and a domain users (Mike / Jane) satisfied.

Please correct me where I'm wrong :)

Looking forward to hearing from you.

François

Archange
  • 397
  • 5
  • 21

1 Answers1

2

I once encountered a similar situation and the approach ended up like this:

Two bounded context: inventory and ordering

When the order requires fullfilling, the ordering context accepts the command, it updates the order from WAIT_FULLFILL to FULLFILLING and emits an OrderFullfillingRequiredEvent.

The inventory context subscribes this event and does inventory update then it emits an InventoryUpdatedEvent with order tracking id.

The ordering context subscribes the latter event and updates the order from FULLFILLING to FULLFILLED.

If someone wants to cancel the order during this period, it will be rejected as the status is now FULLFILLING which indicates the order is waiting for some response.

The equivalent to your use case, I think it would be adding a middle state WAITING_VALIDATION-->VALIDATING-->VALIDATED.

And some compensation is still needed because maybe the InventoryUpdatedEvent may never come. The most annoying issue is on the ui side, maybe you need to poll the backend to make it looks like realtime.

Yugang Zhou
  • 7,123
  • 6
  • 32
  • 60
  • +1 to this. Faced with a similar problem, I would use the Event mechanism to trigger domain activity across bounded contexts. – jett Oct 13 '14 at 01:08
  • Thanks both of you. How do you explain eventual consistency to your domain experts ? (I failed when trying to explain why events were a good fit for us) And can I add an expiration time on the event (to force consistency upon a limit). Ex: my first event has a 2secondes TTL, after that the command handler doesn't treat this one and send a new event EVT_TIMEOUT for example. Seems wrong to you ? – Archange Oct 13 '14 at 12:34
  • @Archange The first question: I just told them this approach may improve system throughout and they said its ok. Sometimes it won't be that simple, but I think focusing on business value improvement is always a good idea. The second question: I had a polling mechanism that the ordering context would resend the event if the order stays at the middle state too long. To achieve this, you need to make the event handlers subscribed *all* idempotent (probably by using an event store). I think your approach also works, the only concern is what if EVT_TIMOUT never goes back. – Yugang Zhou Oct 13 '14 at 14:32