Actors with Apache Pekko
Prerequisites: Futures and concurrency
The actor model handles concurrency without locks: each actor has private state and processes messages one at a time. Apache Pekko is the open-source (Apache 2.0) continuation of Akka, after Akka changed to a commercial licence in 2022.
// illustrative
import org.apache.pekko.actor.typed.{ActorRef, ActorSystem, Behavior}
import org.apache.pekko.actor.typed.scaladsl.Behaviors
object Counter:
enum Command:
case Increment(by: Int)
case Get(replyTo: ActorRef[Int])
def apply(count: Int = 0): Behavior[Command] =
Behaviors.receiveMessage {
case Command.Increment(by) => Counter(count + by)
case Command.Get(replyTo) =>
replyTo ! count
Behaviors.same
}
- Messages are an enum — the typed API checks at compile time that only valid messages are sent.
- State changes by returning a new behavior (
Counter(count + by)), not by mutating variables. !("tell") sends a message without waiting. Because an actor handles one message at a time, its state never needs locking.
Where actors shine
- Stateful, distributed systems — Pekko Cluster spreads actors across machines; Cluster Sharding keeps one actor per entity (per user, per device).
- Event sourcing — Pekko Persistence stores events and rebuilds actor state from them.
- Streaming — Pekko Streams processes unbounded data with back-pressure.
- Resilience — supervisors restart failed actors ("let it crash"), an idea borrowed from Erlang.
For request/response services without long-lived state, an effect system (Cats Effect, ZIO) is usually simpler.