can case object act as a state - scala

My current project is using Java. we push business logics to enum which represents particular state ( New, Registered and so on). In scala, I just wonder that is it a good idea to use case object to act as state ? what is the drawback.
One simple example
trait State {
def read() : Try[String] = {
Failure(new IllegalStateException("too young to read"))
}
}
case object child extends State
case object young extends State {
override def read() : Try[String] = {
Success("young people read book")
}
}
case object elder extends State {
override def read() : Try[String] = {
Success("elder people read book")
}
}
class Person(state : State) {
def read() : Try[String] = {
state.read()
}
}

It can be and in fact it is pretty common pattern (accompanied with sealed trait by the way), but implementation I've seen usually move any actions out of state and use case objects as tags that signify current state, alongside with case classes that may store some state data:
sealed trait State
case class Connected(messagesProcessed: Long) extends State
case object Connecting extends State
case object Disconnected extends State
def foo() = state match {
case Connected(count) => "do something"; state = Connected(count + 1) // could be .copy(..)
case Connecting => "wait patiently"
case Disconnected => "trigger reconnection"
}
Reasoning behind using case classes to store data (versus storing it in plain old class fields) is that different states may have different sets of variables and with case classes it is easier to comprehend current working set.

Related

Akka; Passing around too many parameters with EventSourcedBehavior

We are building a event sourced system based on Akka-Typed. We quickly fall in a situation where our states requires many parameters passed as implicits parameters.
There is a solution on the style guide to use an enclosing class; https://doc.akka.io/docs/akka/current/typed/style-guide.html#passing-around-too-many-parameters.
However this is for "simple" behaviors, not for EventSourcedBehaviors. This technique of using a enclosing class that produce behaviors does not work for event sourced one because an event sourced behavior is made of one command handler and one event handler.
In our case we have a State trait that define one method to received commands and another to handle events. If we apply the enclosing class technique we have to create anonymous classes for all states, but those cannot be serialized.
object SampleEventSourced {
trait State extends CborSerializable {
def execute(cmd: Command): ReplyEffect
def apply(evt: Event): State
}
def apply(
persistenceId: PersistenceId,
config: Config,
): Behavior[Command] = {
EventSourcedBehavior
.withEnforcedReplies[Command, Event, State](
persistenceId,
new SampleEventSourced(config).empty(),
(state, cmd) => state.execute(cmd),
(state, evt) => state.apply(evt)
)// ...
}
}
class SampleEventSourced private(config: Config) {
import SampleEventSourced._
private def empty(): State = new State {
override def execute(cmd: Command): ReplyEffect = cmd match {
// ..
}
override def apply(evt: Event): State = evt match {
// ..
}
}
java.lang.IllegalArgumentException: Empty State [org.acme.SampleEventSourced$$anon$1#36d7d2fe] isn't serializable.
One solution would be to duplicate the creation of the event sourced behavior in each "state" method. But that will produce a lot of duplication.
object SampleEventSourced {
def apply(
persistenceId: PersistenceId,
config: Config,
): Behavior[Command] = new SampleEventSourced(config).empty()
}
class SampleEventSourced private(config: Config) {
import SampleEventSourced._
private def empty(): Behavior[Command] = EventSourcedBehavior
.withEnforcedReplies[Command, Event, State](
persistenceId,
new State(),
(state, cmd) => state.execute(cmd),
(state, evt) => state.apply(evt)
)// ...
}
Another would be to create concrete subclasses of State but we will have to pass the parameters across all of those states.
object SampleEventSourced {
class EmptyState extends State(config:Config, otherUselessParameter:Any) {
// ...
override def apply(evt: Event): evt match {
case _ => new OtherState(config, otherUselessParameter)
}
}
class OtherState extends State(config:Config, veryImportantParameter:Any) {
// ..
}
}
Putting those state classes inside the enclosing class won't work because those non-static inner classes cannot be de-serialized.
So, what's your solution for this case, how do you deal with EventSourcedBehavior with states that require many parameters ?
The problem is that that jackson-cbor, on top of being reflection based, isn't that effective at serializing things done in an idiomatic Scala way (the only real point in its favor is that it's better than Java serialization).
So the immediately apparent solution is to:
model the state as an Algebraic Data Type (generally a sealed trait extended by case classes and maybe a case object for the empty/initial state case).
don't serialize them via Jackson, but use a native-to-Scala serializer (e.g. play-json, circe, etc.) and an Akka serializer leveraging that serializer; it's likely easier to define a marker trait (e.g. CirceSerializable) so that in the configuration you only have to worry about one binding. Events & state will want to be marked with that trait: commands and their replies (at least the ones that could be sent over the network might also as well).
One subtlety to be aware of when defining your serializer is that, the native-to-Scala serializers will generally use some form of typeclass derivation at compile time, but this will be complicated if there's a need to serialize ActorRef, as a serializer for ActorRef is best done with the aid of the ActorSystem (EntityRef can now be inspected for serialization, and conceptually makes more sense for persistence, given the lifecycle, but that's going to be fairly custom): you'll therefore probably want to delay derivation of the ActorRef serializer/deserializer until the ActorSystem starts up and also delay derivation of the events/state/commands/replies containing ActorRefs until after then.
I'd probably tend to make the handler for each event a method in the sealed trait without letting the state have any knowledge of how events are reified. The benefit of this is that you can unit test the state transitions without needing events or commands.
case class ItemAdded(name: String) extends Event
sealed trait State {
def items: Set[String]
def addItem(name: String): State.NonEmpty
}
object State {
case object Empty extends State {
def items: Set[String] = Set.empty
def addItem(name: String): NonEmpty = NonEmpty(Set(name))
}
case class NonEmpty(items: Set[String]) extends State {
def addItem(name: String): NonEmpty = copy(items = items + name)
}
}
Then your event handler is pretty thin
(state, evt) => {
evt match {
case ItemAdded(name) => state.addItem(name)
}
}
Depending on how much validation your command handlers take on, those can get complex. The operative principle is that commands are interpreted into zero-or-more events, and events are interpreted into operations (methods on state which result in a new state) on the state. The state should thus have sufficient query methods (e.g. items) to allow the command-handler to consume a state.

Is there any way to map a Behavior from one type to another

I have a scenario where I need to provide a Behavior of a specific type. This Behavior also needs to handle events that are published on the Event Stream. So say the specific type is:
case class DoSomething(i: Int)
and I then need to implement a function to return a Behavior to handle this type of message:
def foo(): Behavior[DoSomething]
I then also need to handle the following message on the event stream:
case class PublishedEvent(str: String)
The only solution I came up with was to spawn another actor from within my DoSomething behavior and then forward messages to it:
sealed trait Command
case class Command1(str: String) extends Command
case class Command2(str: String) extends Command
def foo(): Behavior[DoSomething] = Behaviors.setup { context =>
val actor = context.spawnAnonymous[Command](Behaviors.setup { context =>
context.system.eventStream ! EventStream.Subscribe(context.messageAdapter {
case PublishedEvent(str) => Command2(str)
})
Behaviors.receiveMessage {
case Command1(str) =>
println("Received Command1: " + str)
Behaviors.same
case Command2(str) =>
println("Received Command1: " + str)
Behaviors.same
}
})
Behaviors.receiveMessage {
case DoSomething(i) =>
actor ! Command1(i.toString)
Behaviors.same
}
}
My question is is there any means of avoiding spawning a new actor and doing it all from within the same actor? i.e. Is there a way I can map a Behavior[Command] to a Behavior[DoSomething]?
I also have this problem regularly and the best solution I found is to use the narrow[...] method on a common "super Behavior" to narrow down on a message type a client is supposed to send to the respective actor. It is possible to combine the handling of both message types in one Behavior by defining a common interface (in scala of course a trait):
sealed trait CommandOrDoSomething // #TODO find better name
final case class DoSomething(i:Int) extends CommandOrDoSomething
sealed trait Command extends CommandOrDoSomething
case class Command1(str: String) extends Command
case class Command2(str: String) extends Command
def getBehavior(): Behavior[CommandOrDoSomething] = Behaviors.setup { context =>
Behaviors.receiveMessage {
// handle all kinds of messages here
}
}
Now, if you want to pass a Behavior[DoSomething] to a client, you can simply obtain it like this:
def foo(): Behavior[DoSomething] = getBehavior().narrow[DoSomething]
You can keep the Command trait private to your current context, but I think you will have to expose at least the common super trait to the outside world. With union types as they will be available in Scala 3 this can be avoided and you won't need the artificial super trait.
The docs (as well as the signature) indicate that transformMessages on Behavior would work, provided that the externally sent messages map 1:1 to internal messages:
def transformMessages[Outer: ClassTag](matcher: PartialFunction[Outer, T]): Behavior[Outer]
i.e. an actor materialized with Behavior[T].transformMessages[Outer] { ??? } will yield an ActorRef[Outer]
sealed trait External
case class DoSomething(i: Int) extends External
private sealed trait Internal
private case class Command1(str: String) extends Internal
private case class Command2(str: String) extends Internal
private def internalBehavior: Behavior[Internal] = Behaviors.setup { context =>
// do stuff with context
Behaviors.receiveMessage {
case Command1(s) => ???
case Command2(s) => ???
}
}
import akka.actor.typed.Behavior.BehaviorDecorators
def externalBehavior: Behavior[External] =
internalBehavior.transformMessages {
case DoSomething(i) => Command1(i.toString)
}
In this case, the internal commands are invisible outside of their scope. The main potential drawback I see (beyond the slight ickiness of bringing a PartialFunction into typed) is that within an actor with Behavior[Internal], you can't get the ActorRef for actor's external "personality", unless you have something crazy like
case class DoSomething(i: Int, recipient: ActorRef[External]) extends External
private case class Command1(str: String, externalPersonality: ActorRef[External]) extends Internal
It's worth noting that in the case where the internal messages are under your control, the 1:1 restriction can be worked around.

Can be PersistentEntity state type a trait?

All examples uses one case class as PersistentEntity state with some property for chech state and use different actions. Something like
override def behavior: Behavior = {
case state if state.isEmpty => initial
case state if !state.isEmpty => postAdded
}
Can be used different case classes of one trait for different states? For example:
sealed trait OrderState
case object UnitializedOrder
case class OpenedOrder(items: List[String])
case object ClosedOrder
override def behavior: Behavior = {
case UnitializedOrder => initOrderActions
case OpenedOrder => openedOrderActions
case ClosedOrder => skipCommandsAction
}
I saw some problems like
Handler types
CommandHandler = PartialFunction[(Command, CommandContext[Any], State), Persist]
EventHandler = PartialFunction[(Event, State), State]
uses State (trait), so needed class casting
Serialization for every state subtype for snapshoting.
Or there is something more scary?

How to construct an actor together with its wrapper?

I'm trying to write an actor called ActorManager which wraps another actor called RealActor. The idea is that the ActorManager can process all messages going in and out of RealActor, allowing to add additional logic like filtering or buffering. The outside world is supposed to communicate with the RealActor only through its manager, much like in real world.
A first draft would look like this:
class ActorManager(realActor: ActorRef) extends Actor {
def receive = {
case _ => { /* pre-process messages */ }
}
}
class RealActor(actorManager: ActorRef) extends Actor {
def receive = {
case _ => { /* actual business logic */ }
}
}
This however raises the question how to construct both actors at once, or more specifically how to define the corresponding Props given the circular dependency of the two actors. I'm not sure if the general lazy val pattern is applicable when defining Props.
I also would like to avoid the work-around of constructing one of the two first, combined with introducing an explicit Register protocol to connect them.
Such a case is much better solved by using an actor hierarchy instead of plain actor siblings. Since the main intent here is to hide the RealActor from the outside world, it makes much more sense that its ActorRef is accordingly wrapped/owned by the outer ActorManager.
This means that the RealActor reference has to be created within the ActorManager scope. This can be achieved by passing the parent an ActorRef => Props function, allowing to create the child actor:
// Parent
class ActorManager(getRealActorProps: ActorRef => Props) extends Actor {
val realActor = context.actorOf(getRealActorProps(self))
def receive = {
case _ => { /* pre-process messages */ }
}
}
// Child
class RealActor(actorManager: ActorRef) extends Actor {
def receive = {
case _ => { /* actual business logic */ }
}
}
object RealActor {
def propsActorManager(getRealActorProps: ActorRef => Props) =
Props(new ActorManager(getRealActorProps))
def propsRealActor(actorManager: ActorRef) =
Props(new RealActor(actorManager))
def props() =
Props(new ActorManager(actorManager => propsRealActor(actorManager)))
}
Note that it is now even possible to hide the fact that the RealActor is wrapped by providing an appropriate props definition, which builds the ActorManager -> RealActor hierarchy implicitly.
See also this blog post why flat actor hierarchies are considered an anti-pattern.
Something of this sort may work well for you:
import akka.actor._
class Manager extends Actor {
def realActor: ActorRef = context.child("real")
.getOrElse(context.actorOf(RealActor.props, "real"))
override def receive: Receive = {
case msg ⇒ realActor forward msg
}
}
object RealActor {
def props: Props = Props(new RealActor)
}
class RealActor extends Actor {
override def receive: Receive = {
case _ ⇒
}
}
Creating child actor through parent's context seems to be sufficient for creating parent-child hierarchy. The parent can be obtained by calling context.parent in child actor.

How do I best share behavior among Akka actors?

I have two Akka actors that respond to some messages in the same way, but others in a different way. They both respond to the same set of messages. Wondering how to design my two actors with their receive methods, via inheritance, composure, etc? I tried chaining together partial functions from other traits with "orElse", which unfortunately exposes the class to its trait's functionality, plus I wasn't sure how the trait's receive could easily access the actor context. A drop-in, modularized solution would be ideal, but I'm wondering if this is a solved problem somewhere?
There's really a bunch of ways that you can go about this. I'll list two from the OO way (similar to what #Randal Schulz is suggesting) and 1 more functional way. For the first possible solution, you could do something simple like this:
case class MessageA(s:String)
case class MessageB(i:Int)
case class MessageC(d:Double)
trait MyActor extends Actor{
def receive = {
case a:MessageA =>
handleMessageA(a)
case b:MessageB =>
handleMessageB(b)
case c:MessageC =>
handleMessageC(c)
}
def handleMessageA(a:MessageA)
def handleMessageB(b:MessageB) = {
//do handling here
}
def handleMessageC(c:MessageC)
}
class MyActor1 extends MyActor{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class MyActor2 extends MyActor{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
With this approach, you define basically an abstract actor impl where the receive function is defined for all messages that are handled. The messages are delegated to defs where the real business logic would be. Two are abstract, letting the concrete classes define the handling and one is fully implemented for a case where the logic does not need to differ.
Now a variant on this approach using the Strategy Pattern:
trait MessageHandlingStrategy{
def handleMessageA(a:MessageA)
def handleMessageB(b:MessageB) = {
//do handling here
}
def handleMessageC(c:MessageC)
}
class Strategy1 extends MessageHandlingStrategy{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class Strategy2 extends MessageHandlingStrategy{
def handleMessageA(a:MessageA) = {}
def handleMessageC(c:MessageC) = {}
}
class MyActor(strategy:MessageHandlingStrategy) extends Actor{
def receive = {
case a:MessageA =>
strategy.handleMessageA(a)
case b:MessageB =>
strategy.handleMessageB(b)
case c:MessageC =>
strategy.handleMessageC(c)
}
}
Here the approach is to pass in a strategy class during construction that defines the handling for a and c, with b again being handled the same regardless. The two approaches are pretty similar and accomplish the same goal. The last approach uses partial function chaining and could look like this:
trait MessageAHandling{
self: Actor =>
def handleA1:Receive = {
case a:MessageA => //handle way 1
}
def handleA2:Receive = {
case a:MessageA => //handle way 2
}
}
trait MessageBHandling{
self: Actor =>
def handleB:Receive = {
case b:MessageB => //handle b
}
}
trait MessageCHandling{
self: Actor =>
def handleC1:Receive = {
case c:MessageC => //handle way 1
}
def handleC2:Receive = {
case c:MessageC => //handle way 2
}
}
class MyActor1 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{
def receive = handleA1 orElse handleB orElse handleC1
}
class MyActor2 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{
def receive = handleA2 orElse handleB orElse handleC2
}
Here, some traits are setup that define message handling behaviors for the 3 message types. The concrete actors mix in those traits and then pick which behaviors they want when building out their receive function by using partial function chaining.
There are probably many other ways to do what you seek, but I just figured I'd throw a few options out there for you. Hope it helps.
So far I have not had reason to regret pushing as much of my services' actual functionality (the so-called "business logic") into a lower layer, "conventional" and synchronous (and sometimes blocking) library that can be unit-tested w/o the complication of actors. The only thing I place in Actor classes is the shared, long-term mutable state on which that conventional library code acts. That, of course, and the message-decoding and dispatching logic of the Akka Actor receive function.
If you do this, sharing logic in the way you seek is trivial.