Kotan Code 枯淡コード

In search of simple, elegant code

Menu Close

Implementing an Asynchronous Repository with Akka Actors

I’ve been fortunate enough to be able to work on an enterprise LOB application that uses the Typesafe stack – Play Framework using Scala and Akka. One of the things that I’ve noticed with application development in Scala is that every morning when I take a look at my code I scan it for opportunities to refactor. I want to know how I can reduce the number of lines of code, which reduces the number of ways it can fail, I want to know how I can make my code cleaner, more elegant, faster, and more scalable.

I was sifting through my code last week and I noticed this little nugget:

ZombieRepository.findByName( zombie.name ).map { ... some stuff ... }

My first thought was that this code looked really old fashioned. Making synchronous method calls like that is just so last year. I immediately saw an opportunity to add a point of scalability and durability by putting my zombie repository (it’s not actually zombies, I’m just using that as an example…) behind a nice abstraction point as well as allow for all kinds of things like load balancing of requests, distributed calls, etc. I decided to implement a repository as an Actor.

The first thing I did, which is part of one of my favorite practices of contract-first design, was build the Actor Protocol.

object ZombieRepositoryActorProtocol {
    case class Insert(zombie: Zombie)
    case class Update(zombie: Zombie)
    case class Delete(zombieId: String)
    case class QueryByName(zombieName: String)
}

At the moment I don’t need to create case classes for the replies (but I could easily do this) because these messages will either be one-way messages or the replies will be things like Vector[Zombie] or Option[Zombie].

In situations where I want to unit test things that rely upon this repository, I need to be able to give the repository a reference to a different actor than the default one we typically reply to (sender), so I can modify the protocol as follows to allow references to the Akka test actor to be passed along and, if it isn’t passed along, then we just reply to sender:

object ZombieRepositoryActorProtocol {
    case class Insert(zombie: Zombie)
    case class Update(zombie: Zombie)
    case class Delete(zombieId: String)
    case class QueryByName(zombieName: String, requester: Option[ActorRef])
}

I could also make the protocol a little more complicated and reply to some of the other operations with status codes or something to allow clients to confirm that an operation completed, but I am keeping it simple for now.

In my own code, the backing store is MongoDB but, another benefit of this type of encapsulation around the repository is that the only thing anybody else knows is how to send messages to the repo which take case classes as arguments. Not only is this a great decoupling away from a persistence medium, but it also allows for all the benefits you get from running Akka actors – you can put the repository behind a round-robin or load-balancing router, you can distribute the repository actors across a grid, and, my personal favorite, you get all the benefit of supervisory control so if your repository fails and crashes, another one can be started up, etc.

So now I can just handle messages like this:

import ZombieRepositoryActorProtocol._

def receive = {
    case Insert(zombie) => ...
    case Update(zombie) => ...
    case Delete(zombieName) => ...
    case QueryByName(zombie, requester) => requester.map { _ }.getOrElse { sender } ! FetchByName(zombie)  
}

I honestly don’t know how I got any large-scale, asynchronous work done before without the use of Akka.