Merge pull request #25137 from akka/wip-24856-EventHandler-alias-patriknw
add EventHandler type alias, #24856
This commit is contained in:
commit
7f8f3c122b
5 changed files with 69 additions and 36 deletions
|
|
@ -29,7 +29,7 @@ private[persistence] final class EventsourcedSetup[C, E, S](
|
|||
val persistenceId: String,
|
||||
val emptyState: S,
|
||||
val commandHandler: PersistentBehaviors.CommandHandler[C, E, S],
|
||||
val eventHandler: (S, E) ⇒ S,
|
||||
val eventHandler: PersistentBehaviors.EventHandler[S, E],
|
||||
val writerIdentity: WriterIdentity,
|
||||
val recoveryCompleted: (ActorContext[C], S) ⇒ Unit,
|
||||
val onSnapshot: (ActorContext[C], SnapshotMetadata, Try[Done]) ⇒ Unit,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ private[akka] final case class PersistentBehaviorImpl[Command, Event, State](
|
|||
persistenceId: String,
|
||||
emptyState: State,
|
||||
commandHandler: PersistentBehaviors.CommandHandler[Command, Event, State],
|
||||
eventHandler: (State, Event) ⇒ State,
|
||||
eventHandler: PersistentBehaviors.EventHandler[State, Event],
|
||||
journalPluginId: Option[String] = None,
|
||||
snapshotPluginId: Option[String] = None,
|
||||
recoveryCompleted: (ActorContext[Command], State) ⇒ Unit = ConstantFun.scalaAnyTwoToUnit,
|
||||
|
|
|
|||
|
|
@ -16,24 +16,47 @@ import scala.util.Try
|
|||
|
||||
object PersistentBehaviors {
|
||||
|
||||
// we use this type internally, however it's easier for users to understand the function, so we use it in external api
|
||||
/**
|
||||
* Type alias for the command handler function for reacting on events having been persisted.
|
||||
*
|
||||
* The type alias is not used in API signatures because it's easier to see (in IDE) what is needed
|
||||
* when full function type is used. When defining the handler as a separate function value it can
|
||||
* be useful to use the alias for shorter type signature.
|
||||
*/
|
||||
type CommandHandler[Command, Event, State] = (ActorContext[Command], State, Command) ⇒ Effect[Event, State]
|
||||
|
||||
/**
|
||||
* Type alias for the event handler function defines how to act on commands.
|
||||
*
|
||||
* The type alias is not used in API signatures because it's easier to see (in IDE) what is needed
|
||||
* when full function type is used. When defining the handler as a separate function value it can
|
||||
* be useful to use the alias for shorter type signature.
|
||||
*/
|
||||
type EventHandler[State, Event] = (State, Event) ⇒ State
|
||||
|
||||
/**
|
||||
* Create a `Behavior` for a persistent actor.
|
||||
*/
|
||||
def receive[Command, Event, State](
|
||||
persistenceId: String,
|
||||
emptyState: State,
|
||||
commandHandler: CommandHandler[Command, Event, State],
|
||||
commandHandler: (ActorContext[Command], State, Command) ⇒ Effect[Event, State],
|
||||
eventHandler: (State, Event) ⇒ State): PersistentBehavior[Command, Event, State] =
|
||||
PersistentBehaviorImpl(persistenceId, emptyState, commandHandler, eventHandler)
|
||||
|
||||
/**
|
||||
* The `CommandHandler` defines how to act on commands.
|
||||
* The `CommandHandler` defines how to act on commands. A `CommandHandler` is
|
||||
* a function:
|
||||
*
|
||||
* {{{
|
||||
* (ActorContext[Command], State, Command) ⇒ Effect[Event, State]
|
||||
* }}}
|
||||
*
|
||||
* Note that you can have different command handlers based on current state by using
|
||||
* [[CommandHandler#byState]].
|
||||
*
|
||||
* The [[CommandHandler#command]] is useful for simple commands that don't need the state
|
||||
* and context.
|
||||
*/
|
||||
object CommandHandler {
|
||||
|
||||
|
|
@ -42,14 +65,16 @@ object PersistentBehaviors {
|
|||
*
|
||||
* @see [[Effect]] for possible effects of a command.
|
||||
*/
|
||||
def command[Command, Event, State](commandHandler: Command ⇒ Effect[Event, State]): CommandHandler[Command, Event, State] =
|
||||
def command[Command, Event, State](commandHandler: Command ⇒ Effect[Event, State]): (ActorContext[Command], State, Command) ⇒ Effect[Event, State] =
|
||||
(_, _, cmd) ⇒ commandHandler(cmd)
|
||||
|
||||
/**
|
||||
* Select different command handlers based on current state.
|
||||
*/
|
||||
def byState[Command, Event, State](choice: State ⇒ CommandHandler[Command, Event, State]): CommandHandler[Command, Event, State] =
|
||||
def byState[Command, Event, State](
|
||||
choice: State ⇒ (ActorContext[Command], State, Command) ⇒ Effect[Event, State]): (ActorContext[Command], State, Command) ⇒ Effect[Event, State] = {
|
||||
new ByStateCommandHandler(choice)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ object PersistentActorCompileOnlyTest {
|
|||
//#command-handler
|
||||
|
||||
//#event-handler
|
||||
val eventHandler: (ExampleState, SimpleEvent) ⇒ (ExampleState) = {
|
||||
val eventHandler: (ExampleState, SimpleEvent) ⇒ ExampleState = {
|
||||
case (state, Evt(data)) ⇒ state.copy(data :: state.events)
|
||||
}
|
||||
//#event-handler
|
||||
|
|
@ -341,30 +341,35 @@ object PersistentActorCompileOnlyTest {
|
|||
if (currentState == newMood) Effect.none
|
||||
else Effect.persist(MoodChanged(newMood))
|
||||
|
||||
val commandHandler: CommandHandler[Command, Event, Mood] = { (_, state, cmd) ⇒
|
||||
cmd match {
|
||||
case Greet(whom) ⇒
|
||||
println(s"Hi there, I'm $state!")
|
||||
Effect.none
|
||||
case CheerUp(sender) ⇒
|
||||
changeMoodIfNeeded(state, Happy)
|
||||
.andThen {
|
||||
sender ! Ack
|
||||
}
|
||||
case Remember(memory) ⇒
|
||||
// A more elaborate example to show we still have full control over the effects
|
||||
// if needed (e.g. when some logic is factored out but you want to add more effects)
|
||||
val commonEffects: Effect[Event, Mood] = changeMoodIfNeeded(state, Happy)
|
||||
Effect.persist(commonEffects.events :+ Remembered(memory), commonEffects.sideEffects)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private val eventHandler: EventHandler[Mood, Event] = {
|
||||
case (_, MoodChanged(to)) ⇒ to
|
||||
case (state, Remembered(_)) ⇒ state
|
||||
}
|
||||
|
||||
PersistentBehaviors.receive[Command, Event, Mood](
|
||||
persistenceId = "myPersistenceId",
|
||||
emptyState = Sad,
|
||||
commandHandler = (_, state, cmd) ⇒
|
||||
cmd match {
|
||||
case Greet(whom) ⇒
|
||||
println(s"Hi there, I'm $state!")
|
||||
Effect.none
|
||||
case CheerUp(sender) ⇒
|
||||
changeMoodIfNeeded(state, Happy)
|
||||
.andThen {
|
||||
sender ! Ack
|
||||
}
|
||||
case Remember(memory) ⇒
|
||||
// A more elaborate example to show we still have full control over the effects
|
||||
// if needed (e.g. when some logic is factored out but you want to add more effects)
|
||||
val commonEffects: Effect[Event, Mood] = changeMoodIfNeeded(state, Happy)
|
||||
Effect.persist(commonEffects.events :+ Remembered(memory), commonEffects.sideEffects)
|
||||
|
||||
},
|
||||
eventHandler = {
|
||||
case (_, MoodChanged(to)) ⇒ to
|
||||
case (state, Remembered(_)) ⇒ state
|
||||
})
|
||||
commandHandler,
|
||||
eventHandler)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package docs.akka.persistence.typed
|
||||
|
||||
import akka.Done
|
||||
import akka.actor.typed.scaladsl.ActorContext
|
||||
import akka.actor.typed.{ ActorRef, Behavior }
|
||||
import akka.persistence.typed.scaladsl.PersistentBehaviors
|
||||
import akka.persistence.typed.scaladsl.PersistentBehaviors.CommandHandler
|
||||
|
|
@ -52,7 +53,7 @@ object InDepthPersistentBehaviorSpec {
|
|||
//#commands
|
||||
|
||||
//#initial-command-handler
|
||||
private def initial: CommandHandler[BlogCommand, BlogEvent, BlogState] =
|
||||
private val initial: (ActorContext[BlogCommand], BlogState, BlogCommand) ⇒ Effect[BlogEvent, BlogState] =
|
||||
(ctx, state, cmd) ⇒
|
||||
cmd match {
|
||||
case AddPost(content, replyTo) ⇒
|
||||
|
|
@ -69,7 +70,7 @@ object InDepthPersistentBehaviorSpec {
|
|||
//#initial-command-handler
|
||||
|
||||
//#post-added-command-handler
|
||||
private def postAdded: CommandHandler[BlogCommand, BlogEvent, BlogState] = {
|
||||
private val postAdded: (ActorContext[BlogCommand], BlogState, BlogCommand) ⇒ Effect[BlogEvent, BlogState] = {
|
||||
(ctx, state, cmd) ⇒
|
||||
cmd match {
|
||||
case ChangeBody(newBody, replyTo) ⇒
|
||||
|
|
@ -94,14 +95,15 @@ object InDepthPersistentBehaviorSpec {
|
|||
//#post-added-command-handler
|
||||
|
||||
//#by-state-command-handler
|
||||
private def commandHandler: CommandHandler[BlogCommand, BlogEvent, BlogState] = CommandHandler.byState {
|
||||
case state if state.isEmpty ⇒ initial
|
||||
case state if !state.isEmpty ⇒ postAdded
|
||||
}
|
||||
private val commandHandler: (ActorContext[BlogCommand], BlogState, BlogCommand) ⇒ Effect[BlogEvent, BlogState] =
|
||||
CommandHandler.byState {
|
||||
case state if state.isEmpty ⇒ initial
|
||||
case state if !state.isEmpty ⇒ postAdded
|
||||
}
|
||||
//#by-state-command-handler
|
||||
|
||||
//#event-handler
|
||||
private def eventHandler(state: BlogState, event: BlogEvent): BlogState =
|
||||
private val eventHandler: (BlogState, BlogEvent) ⇒ BlogState = { (state, event) ⇒
|
||||
event match {
|
||||
case PostAdded(postId, content) ⇒
|
||||
state.withContent(content)
|
||||
|
|
@ -115,6 +117,7 @@ object InDepthPersistentBehaviorSpec {
|
|||
case Published(_) ⇒
|
||||
state.copy(published = true)
|
||||
}
|
||||
}
|
||||
//#event-handler
|
||||
|
||||
//#behavior
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue