How can I ensure all-or-nothing semantics of bulk updates in Lagom services?

I’m considering an API which has a bulk update; if any of the updates fail, the request should fail, if they all succeed, request is OK.

I think a read-side is the most reactive way to do this. However, the issue with the read-side is there could be lag… so if the read-side believes all of the updates could happen but some number fail, there’s no real rollback mechanism

If I model my FooEntity slightly differently, the rollback is no longer needed: instead of each Foo being an entity, the service has an entity which holds Foo objects within its state.

This feels much less reactive, but seems to solve the pervasive issue of Aggregates are Hard in Reactive Systems™. Are there other considerations?

@erip so you have two design options:

  1. FooEntity & FooBatchUpdateSagaEntity
    Entity instance per Foo instance (Foo instance actions done directy on FooEntity).
    Batch update action done via FooBatchUpdateSagaEntity, instanced per batch request, responsible for sending update comands to FooEntity. In case of fail doing rollback on successfully updated Foos.

Pros: scalable, FooEntity behavior simpler, (batch and single actions decoupled)

Cons: asynchron (no batch status in response for batch update request), generaly more complexed because of addition Saga entity

  1. FoosEntity
    One entity instance with list of Foo instances

Pros: “synchron” (batch status in response for batch update request), generally less complexed

Cons: not scalable, FooEntity behavior more complexed because of list maintaining (batch and single actions coupled)

I beleive scalability is a biggest issue.
I always took a save side and used design #2 and always sacrificed response having update status.

But again each of this design options have there use case depending on the use case requirements. Also, in some use cases, mentioned design cons are not necessary cons.