CLOVIS
05/31/2024, 11:01 AMThe user A does the action a.
The user B is then asked to accept or refuse.
If the user B accepted, the user A is asked to confirm or cancel their approval.
Then, the entity on which the actions were made is marked as done.
However, we are often working on distributed applications (client-server, replicated servers...). We often end up using event-based architectures, which leads to this kind of code:
fun onActionA(...) {
require(currentUser == A)
// the process here
entity.state = Created(by = currentUser)
}
fun onAcceptOrRefuse(...) {
check(currentUser == B)
check(entity.state is Created)
// The process here
entity.state = Accepted(by = currentUser)
}
...
This works, but it's particularly verbose and it obscures the algorithm. It makes everything harder to maintain and harder to debug: where do events come from?
Instead, I'd like to re-use the suspend
keyword to generate the state machine implicitly:
suspend fun process() {
val initial = await(actionA from A)
service.create(...)
val acceptation = await(AcceptOrRefuse from B)
if (acceptation.accept) {
val confirmation = await(ConfirmOrCancel from A)
if (confirmation.confirmed) {
service.markConfirmed(...)
} else {
service.markCancelled(...)
}
} else {
service.markRefused(...)
}
service.markDone(...)
}
This would make it much easier to understand what the process is. And, it's not initially that complex to implement!
The problem is: how do you resume this system? If the server dies, and thus the coroutine disappears, how to re-create the coroutine at the same point it was previously, so the process isn't interrupted?CLOVIS
05/31/2024, 11:02 AMAdam S
05/31/2024, 12:49 PMAdam S
05/31/2024, 12:53 PMCLOVIS
05/31/2024, 1:00 PM