like-persistence (cqrs?) 事件采购和副作用
akka-persistence (cqrs?) eventsourcing and side effects
我正在尝试弄清楚如何使用持久性参与者对远程 "IoT" 设备的状态进行建模,例如:
用户想开一盏灯,所以我们最合乎逻辑。
- 用户发送
OnCommand
- persistent actor 接收命令,生成
LightTurnedOnEvent
并将其状态更新为 on
这是有道理的,但这里的问题是灯实际上从未打开过。好的,那么我们构建一个知道低级硬件控制巫术的 LightControlActor
。这个 actor 监听 LightTurnedOnEvent
,当它得到它时它会做事并打开灯。
真棒,现在我们开了一盏灯!但是不开心。 LightTurnedOnEvent
有点躺在这里,还没开灯呢。按照这个逻辑 LightTurnedOnEvent
应该由 LightControlActor
生成,我的持久演员应该生成一些 SentRequestToTurnOnLight
但现在这对我来说变得复杂了所有不同的语义。
- 用户发送
OnCommand
持久性 actor 接收 OnCommand
生成 RequestedLightTurnOnEvent
并将状态设置为挂起。
LightController 在 RequestedLightTurnOnEvent
上启动并尝试打开外部系统上的灯。
然后呢?现在我该如何更新持久化 actor 的状态?让 LightController 发送一些奇怪的命令 SetStateToOnCommand
?
那么当灯实际打开时如何更新持久状态?
一个想法是为您的活动使用 "saga" 之类的东西。
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)
这为您提供了细粒度的控制。万一死机,在恢复过程中,可以查看灯是否亮起,或者LightController
是否处于pending状态。如果它处于挂起状态,您可以重新发送 LightTurnOnCommand
.
但您永远无法确定灯是否亮起,即使硬件控制器这样说,即灯泡可能已损坏。
关于事件的想法是它们在 有界上下文 中的含义。在您的情况下, LightTurnedOnEvent
由持久性参与者拥有,并且仅在其上下文中具有严格的含义。从所有者 actor 的 POV 来看,灯 应该 亮起并且未来 TurnOnCommand
不会改变此状态,不会发出新事件(它是幂等的)。
如果您希望此事件有其他含义,您需要一个 Saga(或任何您想称呼它的东西),它会对此事件做出反应,并将另一个命令发送给另一个 actor,在另一个上下文中,即在硬件上下文。这个 HW actor 会发出自己的事件,仅在其上下文中相关,即 LightBulbCoupledToPowerSource
或类似的东西;它甚至可以像另一个一样被命名为 LightTurnedOnEvent
,但是在一个单独的 命名空间 中,很可能与其他属性一起命名。
为了更好地了解 concerns/contexts 的这种分离,我们可以想象这样一种情况,即 Saga 响应 LightTurnedOnEvent
会向 3 个不同的灯泡发送 3 个命令,在一个大房间里例如。
我正在尝试弄清楚如何使用持久性参与者对远程 "IoT" 设备的状态进行建模,例如:
用户想开一盏灯,所以我们最合乎逻辑。
- 用户发送
OnCommand
- persistent actor 接收命令,生成
LightTurnedOnEvent
并将其状态更新为on
这是有道理的,但这里的问题是灯实际上从未打开过。好的,那么我们构建一个知道低级硬件控制巫术的 LightControlActor
。这个 actor 监听 LightTurnedOnEvent
,当它得到它时它会做事并打开灯。
真棒,现在我们开了一盏灯!但是不开心。 LightTurnedOnEvent
有点躺在这里,还没开灯呢。按照这个逻辑 LightTurnedOnEvent
应该由 LightControlActor
生成,我的持久演员应该生成一些 SentRequestToTurnOnLight
但现在这对我来说变得复杂了所有不同的语义。
- 用户发送
OnCommand
持久性 actor 接收
OnCommand
生成RequestedLightTurnOnEvent
并将状态设置为挂起。LightController 在
RequestedLightTurnOnEvent
上启动并尝试打开外部系统上的灯。
然后呢?现在我该如何更新持久化 actor 的状态?让 LightController 发送一些奇怪的命令 SetStateToOnCommand
?
那么当灯实际打开时如何更新持久状态?
一个想法是为您的活动使用 "saga" 之类的东西。
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)
这为您提供了细粒度的控制。万一死机,在恢复过程中,可以查看灯是否亮起,或者LightController
是否处于pending状态。如果它处于挂起状态,您可以重新发送 LightTurnOnCommand
.
但您永远无法确定灯是否亮起,即使硬件控制器这样说,即灯泡可能已损坏。
关于事件的想法是它们在 有界上下文 中的含义。在您的情况下, LightTurnedOnEvent
由持久性参与者拥有,并且仅在其上下文中具有严格的含义。从所有者 actor 的 POV 来看,灯 应该 亮起并且未来 TurnOnCommand
不会改变此状态,不会发出新事件(它是幂等的)。
如果您希望此事件有其他含义,您需要一个 Saga(或任何您想称呼它的东西),它会对此事件做出反应,并将另一个命令发送给另一个 actor,在另一个上下文中,即在硬件上下文。这个 HW actor 会发出自己的事件,仅在其上下文中相关,即 LightBulbCoupledToPowerSource
或类似的东西;它甚至可以像另一个一样被命名为 LightTurnedOnEvent
,但是在一个单独的 命名空间 中,很可能与其他属性一起命名。
为了更好地了解 concerns/contexts 的这种分离,我们可以想象这样一种情况,即 Saga 响应 LightTurnedOnEvent
会向 3 个不同的灯泡发送 3 个命令,在一个大房间里例如。