Introduction to Akka Typed with Scala for beginners.

Jay
8 min readDec 14, 2020

Learn to Build distributed and fault tolerant systems with me and Dead-pool.

Learn best in class technology upon which many cutting edge frameworks are build(Play Framework, lagom,cloudFlow, etc…) the list goes on…
I would like to share my insights on how to get there.when i started my journey to learn Scala and Akka, i had to refer numerous articles and videos to get where i am today, i don't want the beginners to go through the same pain, these articles are created by keeping complete get go stuff in mind.

What is Akka and why you should learn it with Scala?

ever wondered how to build highly distributed and complex systems with ease and type safety? then why wait ? continue reading…
Akka is a “toolkit” for building highly
concurrent( ability of different parts or units of a program, algorithm, or problem to be executed out-of-order or in partial order, without affecting the final outcome.),
distributed (components are located on different networked computers, which communicate and coordinate their actions by passing messages to one another), and
resilient(tending to recover from or adjust easily to change) message-driven applications with actors as bare-bones of the implementation.
Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries

Simplified- Akka is Great, Scala is Awesome!!!!

How it started(Akka Classic) / How its going(Akka Typed)

Akka was introduced as Akka Classic , “Classic” Actors are untyped. You define an actor that accepts Any object, and use pattern matching to define which messages it will actually handle.It will not warn you from sending message to a wrong actor ,messages will be ignored or sent to Dead Letters. You can send virtually any type of message to any actor.it was hard to isolate problems in few scenarios.

Akka Typed is Type safe and wrapped with variance, and we have Behavior to handle messages which may either be of the type that the Actor declares and which is part of the ActorRef signature or it may be a system Signal that expresses a life cycle event of either this actor or one of its child actors.this helps in isolating the problems at compile time and avoid failures and dead letters.

What the hell is an Actor?

Actors date back to 1973 with main goal is to overcome tedious and error-prone threaded systems which were a part of legacy Monoliths. no matter how much you try, systems end up with deadlocks(System hangs). in distributed world this model will not help overcome challenges with modern thread-safe needs.
Actors on the other hand are a simplified approach to overcome this challenge, everything that happens in an actor driven environment is via messages. Yes! you heard it right, just by messages nothing more, you pass a message as a behavior/plain text to actorSystem by defining how the actor should alter a state of another actor/send a response/create more actors to take care of load, everything is achieved using messages. we cannot modify internal state of the actors but can just tell them what to do.

Why use Scala?

before we proceed i am writing this article in Scala because,
1- Akka is build using Scala .
2- Scala is more functional and type safe compared to JAVA ,easy to understand and maintain.
3- and my unconditional love towards the language and community.

Stuff gets interesting from here😍.

Have you watched Dead Pool?
Yes, Dead Pool…. i believe learning should be fun and who is more humorous than dead pool, all the code which you are about to see in upcoming articles is related to the comic conventions so please pardon my comicality if you haven't watched the movie.

Just like dead pool is a mutant with extraordinary regenerative mutation, no matter what you do to him, he will get back to normal. cut off his legs, he will grow a new pair, stab him he will recover.
Akka is designed for handling failures in a similar way in distributed environment, it applies the “let it crash” philosophy: instead of recovering and correcting internal state that may have become invalid because of the failure with the underlying logic we move to a totally different approach. For many cases the fix can be to “crash” the actor, and start a new one, with a fresh state that we know is valid.

I am trying to keep the code complexity to minimal in this article and share more details on how to’s and best practices,more complex examples are about to be published soon, stay tuned.
without further ado, lets begin…

1- Building the Project

if you are into Scala then i recommend sbt and intelliJ to create initial build of the project and start coding by appending required dependencies.

for the project to initialize you need to install sbt in your windows, if done ignore this part

create your Scala-Build -

if everything builds up correctly , you will see this page.

Expand the project folder and go to build.sbt
Add below code snippet to the file and click on load sbt changes.

val akkaVersion = "2.6.10"

libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor-typed" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"ch.qos.logback" % "logback-classic" % "1.2.3"
)
add the code snippet and hit load sbt changes.

Create main .scala File

Expand project folder > src/main/scala
right click on scala package and go to new and select scala class and provide file name and create object from the menu

Before we start implementing the project i would like to suggest few best practices if you are new to Scala or already into Scala but were always confused on how to initialize the API.

traits and case classes

traits are like skeletons to extend/creating small groups of constants by adding them as mixins
if any class/case class is extended by trait then it has to use all the methods/functions defined in the trait except if the methods/functions are Pre-defined they will be auto imported(You can override the methods/functions if you want which is a whole different topic “Abstract Classes”)

enumerations is all you do before we start implementing the logic into the project

Case-classes are good for modeling immutable data

a case class has all of the functionality of a regular class, and more. When the compiler sees the case keyword in front of a class, it generates code for you, with the following benefits:
Case class constructor parameters are public Val fields by default, so accessor methods are generated for each parameter.
an apply method is created in the companion object of the class, so you don’t need to use the new keyword to create a new instance of the class.
an unapply method is generated, which lets you use case classes in more ways in match expressions.
a copy method is generated in the class. You may not use this feature in Scala/OOP code, but it’s used all the time in Scala/FP.
equals and hashcode methods are generated, which let you compare objects and easily use them as keys in maps.
a default toString method is generated, which is helpful for debugging.

Required Imports

import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import scala.concurrent.ExecutionContextExecutor

Actor API/Protocol

declare traits and case classes as a part of enumerations
sealed trait — A sealed trait can be extended only in the same file as its declaration.

sealed trait MeanPool
case class comments(what:String) extends MeanPool

Define the Behavior in the main app-

object DeadPoolActor extends App{
def apply(): Behavior[MeanPool] = {
Behaviors.receive {
case (context, comments(comment)) =>
context.log.info(comment)
Behaviors.same
}
}

You can create behavior by declaring Behaviors._ factory with apply method.
apply is a function that applies a function to arguments”.

Behaviors come with different message handling structure which replaces _ with below examples.
The behavior of an actor defines how it reacts to the messages that it receives.
behavior always returns behavior,
1- Behaviors.receive = receives (context, message) or with only 1 argument when context is already accessible by other means.
2- Behaviors.receiveMessage = receives only message.
3- Behaviors.receivePartial = actor `Behavior` from a partial message handler which treats undefined messages as unhandled.
4-Behaviors.setup = `setup` is a factory for a behavior. Creation of the behavior instance is deferred until the actor is started, as opposed to [[Behaviors.receive]] that creates the behavior instance immediately before the actor is running. `setup` is typically used as the outer most behavior when spawning an actor, but it can also be returned as the next behavior when processing a message or signal.

and many other per-defined methods, that's the beauty of Akka toolkit it comes with all the predefined models and supporting types.

use pattern matching to handle different types of messages and exceptions.
Pattern matching is a mechanism for checking a value against a pattern. A successful match can also deconstruct a value into its constituent parts.
log messages to console to display results with the help of actor context.

Behaviors.same = Return this behavior from message processing in order to advise the system to reuse the previous behavior, including the hint that the message has not been handled.

Create ActorSystem

implicit val system: ActorSystem[MeanPool] = ActorSystem(DeadPoolActor(), "DeadPoolActor1")

ActorSystem is the main for managing actors, logging, scheduling, watching, etc.

Actor System takes guardianBehavior with type Behavior[Type] and a name

ActorSystem(guardianBehavior: Behavior[T], name: String)

always declare system as implicit , implicits are syntactic sugar of Scala.
when val are marked by the implicit keyword at the start of the parameter list. If the parameters in that parameter list are not passed as usual, Scala will look if it can get an implicit value of the correct type, and if it can, compiler will pass it automatically.

Send Messages to Actor System

val findFrancis: ActorRef[MeanPool] = system

findFrancis
! comments("Maximum Effort")

assign the system to a val so that we can pass messages to the system , in-turn system will handle the actor Behavior for the API.

Output

Output

There, our actor is returning results and dead pool is searching for Francis.
lets hope he will find him , if not he might do some nasty stuff.

Complete Code Snippet

import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.actor.typed.scaladsl.Behaviors

sealed trait MeanPool
case class comments(what:String) extends MeanPool

object DeadPoolActor extends App{
def apply(): Behavior[MeanPool] = {
Behaviors.receive {
case (context, comments(comment)) =>
context.log.info(comment)
Behaviors.same
}
}
implicit val system: ActorSystem[MeanPool] = ActorSystem(DeadPoolActor(), "DeadPoolActor1")

val findFrancis: ActorRef[MeanPool] = system

findFrancis
! comments("Maximum Effort")
}

There! all caught up.

check out our page YolkOrbit for more posts,

can’t wait to simplify and explain more advanced topics for you, get back for more for articles on PlayFramework,Akka, Akka Streams, Akka HTTP with Scala.

Cheers😎🍻.

--

--