From aceb4d6aa3d67f532c63b1ec8e48bf2cc00cc440 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 23 May 2018 18:47:29 +0200 Subject: [PATCH] add EventHandler type alias, #24856 * for completeness, since we have one for CommandHandler, and sometimes it might be useful with the shorter type signature * use the explicit function type for CommandHandler in API signatures, because it's easier to see what it actually is --- .../typed/internal/EventsourcedSetup.scala | 2 +- .../internal/PersistentBehaviorImpl.scala | 2 +- .../typed/scaladsl/PersistentBehaviors.scala | 35 +++++++++++-- .../PersistentActorCompileOnlyTest.scala | 49 ++++++++++--------- .../typed/InDepthPersistentBehaviorSpec.scala | 17 ++++--- 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventsourcedSetup.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventsourcedSetup.scala index 3b23f21c44..9f23f27325 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventsourcedSetup.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/EventsourcedSetup.scala @@ -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, diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/PersistentBehaviorImpl.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/PersistentBehaviorImpl.scala index 399f521aab..11ea532309 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/PersistentBehaviorImpl.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/PersistentBehaviorImpl.scala @@ -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, diff --git a/akka-persistence-typed/src/main/scala/akka/persistence/typed/scaladsl/PersistentBehaviors.scala b/akka-persistence-typed/src/main/scala/akka/persistence/typed/scaladsl/PersistentBehaviors.scala index 751992d3e3..518be6efac 100644 --- a/akka-persistence-typed/src/main/scala/akka/persistence/typed/scaladsl/PersistentBehaviors.scala +++ b/akka-persistence-typed/src/main/scala/akka/persistence/typed/scaladsl/PersistentBehaviors.scala @@ -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) + } } diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PersistentActorCompileOnlyTest.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PersistentActorCompileOnlyTest.scala index e799c9b6f9..9cd7500ea1 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PersistentActorCompileOnlyTest.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/PersistentActorCompileOnlyTest.scala @@ -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) } diff --git a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/InDepthPersistentBehaviorSpec.scala b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/InDepthPersistentBehaviorSpec.scala index f1bf0fb63b..e64f5b1e46 100644 --- a/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/InDepthPersistentBehaviorSpec.scala +++ b/akka-persistence-typed/src/test/scala/docs/akka/persistence/typed/InDepthPersistentBehaviorSpec.scala @@ -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