Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Shopping Basket

To convert the Shopping Basket system from Akka typed actors to untyped actors, we need to modify the code so that it aligns with the untyped actor model in Akka. Below is the step-by-step process to achieve this.

Step 1: Define Messages and Data Models

The message and data model definitions remain mostly the same, but we'll omit the sealed trait for commands, as untyped actors don't enforce the use of sealed trait in the same way.

// Messages
case class AddItem(item: Item)
case object Checkout

// Data Models
case class Item(name: String, price: BigDecimal)
case class Receipt(items: List[Item], total: BigDecimal)

Step 2: Define the BasketActor

Now, let's define the BasketActor. In untyped actors, we extend the Actor trait and implement the receive method. The actor's state can be managed as mutable variables inside the actor.

import akka.actor.{Actor, ActorLogging, Props}

class BasketActor extends Actor with ActorLogging {
  var items: List[Item] = List.empty

  override def receive: Receive = {
    case AddItem(item) =>
      log.info(s"Item added to basket: ${item.name}")
      items = items :+ item

    case Checkout =>
      val total = items.map(_.price).sum
      log.info(s"Checking out with total: $total")
      // In a real application, you might want to send the receipt to another actor or persist it.
      context.stop(self)
  }
}

object BasketActor {
  def props(): Props = Props(new BasketActor)
}

Step 3: Set Up the Actor System and Test

Finally, let's set up the ActorSystem, create the BasketActor, and send messages to test the functionality. The main difference in untyped actors is that we use system.actorOf to create actors and ! (tell) to send messages.

import akka.actor.{ActorSystem, Props}

object ShoppingApp extends App {
  // Create Actor System
  val system = ActorSystem("shoppingSystem")

  // Create BasketActor
  val basketActor = system.actorOf(BasketActor.props(), "basketActor")

  // Interact with the BasketActor
  basketActor ! AddItem(Item("Apple", BigDecimal("0.60")))
  basketActor ! AddItem(Item("Banana", BigDecimal("0.40")))
  basketActor ! Checkout

  // Shutdown the actor system after a short delay to see the output.
  Thread.sleep(1000) // Not ideal for real applications, just for demonstration
  system.terminate()
}

Explanation:

  • Actor Creation: In untyped actors, BasketActor is created using system.actorOf(BasketActor.props(), "basketActor").
  • Message Handling: The receive method handles incoming messages by pattern matching on the message type.
  • State Management: State (i.e., the list of items) is maintained as a mutable variable inside the actor.
  • Logging: The ActorLogging trait is mixed in to provide easy access to logging within the actor.