0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Designing Actor Systems

Learn to design and implement actor hierarchies, define actor behaviors, and send/receive messages.

Designing Actor Systems is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 2 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Design Actor Systems: Intro

Welcome to Lesson 2! In the previous lesson, we learned the basics of the Akka Actor Model. Now, let's dive into designing Akka Actor Systems.

A well-designed actor system is robust, scalable, and easy to maintain. We'll explore how to structure your actors, define their behaviors, and enable them to communicate effectively.

Understanding Actor Hierarchies

Akka actors are organized in a tree-like hierarchy, much like a file system. Every actor, except the 'root' actor, has a parent.

  • Parent-Child Relationship: An actor that creates another actor becomes its parent.
  • Supervision: This hierarchy is crucial for Akka's fault tolerance. Parents are responsible for supervising their children (more on this in the next lesson!).
  • Path-based Addressing: Each actor has a unique path, reflecting its position in the hierarchy, like /user/parentActor/childActor.

Setting Up Your ActorSystem

The ActorSystem is the entry point for all Akka applications. It manages the resources needed for actors, including thread pools and schedulers.

You create it once per application and then use it to create your top-level actors. It's like the container for your entire actor application.

Try running this simple code to create an ActorSystem:

import akka.actor.ActorSystem

object AkkaSystemApp {
  def main(args: Array[String]): Unit = {
    // Create an ActorSystem with a unique name
    val system = ActorSystem("MyFirstActorSystem")
    println("ActorSystem created: " + system.name)

    // Remember to terminate the system when done!
    system.terminate()
  }
}

Defining Actor Behavior

An actor's behavior determines how it reacts to different messages. In Scala, you define this by extending the Actor trait and implementing the receive method.

  • The receive method is a partial function that matches incoming messages to specific actions.
  • Each actor processes messages one by one, sequentially, ensuring thread safety for its internal state.

ActorRef: The Actor's Address

You don't directly interact with actor instances. Instead, you send messages to an ActorRef. Think of ActorRef as a postal address for an actor.

  • It's a handle that represents a specific actor within the system.
  • Messages sent to an ActorRef are placed in the actor's mailbox.
  • You obtain an ActorRef when you create an actor using actorOf.

Sending Messages to Actors

The primary way actors communicate is by sending messages. The most common way to send a message is using the ! (pronounced 'tell') method.

receiverActorRef ! "Your message here"

The tell method is asynchronous and fire-and-forget. It sends the message and doesn't wait for a reply.

Let's combine creating an actor and sending it messages:

import akka.actor.{Actor, ActorRef, Props, ActorSystem}

// 1. Define the actor's behavior
class GreeterActor extends Actor {
  def receive = {
    case "hello" => println(s"Greeter: Hello from ${self.path.name}!")
    case msg: String => println(s"Greeter: Received message: '$msg'")
    case _ => println("Greeter: Received something unexpected!")
  }
}

object SimpleActorApp {
  def main(args: Array[String]): Unit = {
    // 2. Create the ActorSystem
    val system = ActorSystem("GreeterSystem")

    // 3. Create a top-level actor and get its ActorRef
    val greeter: ActorRef = system.actorOf(Props[GreeterActor], "myGreeter")
    
    // 4. Send messages to the actor using '!'
    greeter ! "hello"
    greeter ! "How are you doing today?"
    greeter ! 123 // This will match the _ case

    // Give actors time to process messages, then terminate
    Thread.sleep(1000)
    system.terminate()
  }
}

Creating Child Actors

Actors can create other actors, forming the hierarchy we discussed. A parent actor creates its child actors using context.actorOf.

  • context.actorOf: Used by an actor to create its children.
  • system.actorOf: Used by the application to create top-level (guardian) actors.

Creating child actors helps in organizing your system, delegating tasks, and isolating failures.

Parent-Child Communication

Actors communicate by sending messages to each other's ActorRef. A child actor can get its parent's ActorRef via context.parent. To reply to the sender of a message, an actor can use sender().

Let's see an example where a parent creates a child, tells it to do work, and the child reports back when done:

import akka.actor.{Actor, ActorRef, Props, ActorSystem}
import scala.concurrent.ExecutionContext.Implicits.global

// Child Actor Definition: Does work and replies to sender
class ChildActor extends Actor {
  def receive = {
    case "work" =>
      println(s"ChildActor (${self.path.name}): Doing work from ${sender().path.name}")
      sender() ! "done" // Reply 'done' to the sender (parent)
  }
}

// Parent Actor Definition: Creates child, sends work, receives reply
class ParentActor extends Actor {
  // Create a child actor when the parent starts
  val child: ActorRef = context.actorOf(Props[ChildActor], "myChild")

  def receive = {
    case "start" =>
      println("ParentActor: Kicking off child's work.")
      child ! "work" // Tell the child to do 'work'
    case "done" =>
      println(s"ParentActor: Child ${sender().path.name} reported 'done'.")
      context.stop(child) // Stop the child actor
      context.stop(self)   // Stop the parent actor
  }
}

object HierarchyApp {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("HierarchySystem")
    val parent: ActorRef = system.actorOf(Props[ParentActor], "parentActor")
    parent ! "start" // Start the interaction

    // Wait for the system to terminate (actors will stop themselves)
    system.whenTerminated.onComplete(_ => println("ActorSystem terminated."))
  }
}

Stopping Actors Gracefully

Actors should be stopped gracefully when they are no longer needed to release resources. You can stop an actor in a few ways:

  • context.stop(self): An actor stops itself.
  • context.stop(childRef): A parent stops one of its children.
  • system.stop(actorRef): For top-level actors, though usually, you stop the entire system.

When an actor stops, it processes all messages in its mailbox before terminating. Its children are also stopped recursively.

Quick Check: Actor Design

Consider the following Akka actor code snippet. What is the correct way for MyChildActor to send a message back to its creating parent, MyParentActor, if MyParentActor was the sender of the initial 'request' message?

Recap: Designing Actor Systems

Great job! In this lesson, we've covered key aspects of designing Akka Actor Systems:

  • Actors are organized in hierarchies, which are vital for supervision and structure.
  • The ActorSystem is your application's entry point for Akka.
  • You define actor behavior using the receive method.
  • ActorRef is the address for sending messages.
  • Actors communicate asynchronously using the ! (tell) method.
  • Parents create children with context.actorOf, and children can reply using sender().
  • Actors can be stopped gracefully using context.stop().

Next, we'll delve deeper into Akka's powerful supervision strategies and how to handle failures gracefully!

Frequently asked questions

Is the “Designing Actor Systems” lesson free?

Yes — the full text of “Designing Actor Systems” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 3 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.

What will I learn in “Designing Actor Systems”?

Learn to design and implement actor hierarchies, define actor behaviors, and send/receive messages. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Scala for Backend Engineering & Functional Programming?

No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 3, so you can start here or from the beginning and move at your own pace.

How long does the “Designing Actor Systems” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?

Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Akka Actor Model Fundamentals
  2. Designing Actor Systems
  3. Supervision and Fault Tolerance
← Back to Scala for Backend Engineering & Functional Programming