Search code examples
scalaakkaevent-sourcingakka-persistenceakka-typed

akka-persistence (cqrs?) eventsourcing and side effects


I'm trying to figure out how to model the state of a remote "IoT" device with a persistent-actor, for example:

The user wants to turn on a light so we do the most logical.

  1. User sends OnCommand
  2. the persistent actor receives the command, generates a LightTurnedOnEvent and updates its state to on

So that makes sense but the problem here is the light is actually never turned on. Ok so then we build a LightControlActor that knows low level hardware-controll voodoo. This actor listen for LightTurnedOnEvent and when it gets it it does it thing and turnes on the light.

Awsome now we have a light turned on! But not happy. LightTurnedOnEvent is kind of lying here, the light is not turned on yet. Following this logic LightTurnedOnEvent should be generated by the LightControlActor and my persistent actor should generate some SentRequestToTurnOnLight but now this gets complicated for me with all the different semantics.

  1. User sends OnCommand
  2. Persistent actor recivecs OnCommand generate RequestedLightTurnOnEvent and set state to pending.

  3. LightController picks up on the RequestedLightTurnOnEvent and tries to turn on the light on the external system.

So then what? Now how do I update the state of the persistent actor? Have the LightController send some veird command SetStateToOnCommand?

So how do I update the persistent state when the light is actually turned on?


Solution

  • One idea is to go with something like "saga" for your events.

    LightController: State idle
    lightController ! OnCommand
        persist(LightTurnOnAttempted)
        lightControl ! LightTurnOnCommand
        become(pending)
    
    LightControl:
    lightControl ! LightTurnOnCommand
        performLightTurnOnAsyncFunction.map(_ => TurnOnLightCommand) pipeTo lightController
    
    LightController: State pending
    lightController ! TurnOnLightCommand
        persist(LightTurnedOn)
        become(initialized)
    

    This gives you fine grained control. In case of crash and during the process of recovery, you can check whether the light was turned on or the LightController was in pending state. If it was in pending state, you can resend the LightTurnOnCommand.