Persisting events as the result of other events

(James Harlow) #1

Hi all, we’ve run into a pattern a few times that we can’t find a clean solution to. We have an object that starts off in a state {} and receives messages x, y, and z, to become {x, y, z}. When it’s received all of these messages we want it to fire an event to say that it’s finished changing state - we don’t want to push the responsibility of knowing what “finished” is to its downstream services.

Concretely, then, our Entity looks a little like this (excuse the overloading, hopefully it’s clear):

type EntityState = (Option[X], Option[Y], Option[Z])

  .onCommand[X, Done] {
    case (x, ctx, (None, Some(y), Some(z))) => 
        Finished(x, y, z)
       ) { _ => ctx.reply(Done) }
    case (x, ctx, _) => ctx.thenPersist(X(x)) { _ => ctx.reply(Done) }
  .onCommand[Y, Done] { /* mutatis mutandis */ }
  .onCommand[Z, Done] { /* mutatis mutandis */ }
  .onEvent {
    case (X(x), (_, y, z)) => (Some(x), y, z)
    // etc.
    case (Finished(x, y, z), state =>"yay"); state 

It feels very awkward to put the logic in the command handler rather than the event handler - we’re essentially anticipating the event without actually having seen the cause of it committed. We think that it’s safe to do so (that all readers of the topic will see the X event before the Finished event) but we’re not sure this is true. In addition we’ve got to be really careful with the logic (we don’t want two Finished events to fire).

Are there any better patterns we could follow here? We’ve thought of:

  • Setting up a subscriber to the entity’s topic and posting a command based on the messages it sees - this means a huge amount of duplicated logic.
  • trying to make com.lightbend.lagom.scaladsl.persistence.Persist monadic - this didn’t seem to have legs as it’s internal
  • reaching outside Lagom and posting the Finished event directly to Kafka - this seemed like a cure worse than the disease.

Has anyone else come across this and found better ways to do this than us?

(Alan Klikic) #2

If i understand correctly you a service receiving messages and when when messages x,y,z are received you need to publish on service topic Finished event.
For me the most natural Lagom way is to implement it using Entity (like your example).
Message is a command that is persisting event and updating state with info which message was received. Command handler is checking the state if finished and if true persists event Finished that is then published to topic.

Event handler is used only to update state and it is triggered on every Entity restore. So event handler is not a place to put any kind of decision logic.
On the other hand command handler is the one that is persisting (creating) events and this is where decision login should be.

Hope this helps