You mentioned, that you are using companion object
s as a services. I also noticed, that you are
creating actors inside of the object
s. In general I will discourage you from doing this. Scala (companion) object
s
are just singletons. While they can be useful and appropriate in some circumstances, in general they are considered to be
an anti-pattern rather than a pattern, especially if you want to do dependency injection or inversion of control in
your application. There are a lot of reasons for this, but the most important ones in this case are: it's hard to mock them,
it's hard to control their instantiation, and in general they represent an opposite of inversion of control.
Another problem, is that you are creating actors inside of these singleton objects. Very important aspect of actor model is
supervision hierarchy. By creating this actor
(UserService
in your case) in isolation, you most probably let guardian actor to be it's supervisor, which in most case
is not what you want. So I would recommend to create most of the actors within another actors, except few,
that need to be top-level actors. This will make sure that they have proper supervision hierarchy.
These ideas also remain the same if you are using Scaldi. scaldi-akka provides
convenient way to inject an ActorRef
or Props
for some particular actor. Here is a small example of how you can
inject normal bindings and ActorRefs
:
class ProfileManager (implicit inj: Injector) extends Injectable
trait UserManager {
def register(email: String, password: String): User
}
class UserManagerImpl(implicit inj: Injector) extends UserManager with Injectable {
val profileManager = inject [ProfileManager]
def register(email: String, password: String) = ???
}
class UserService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = inject [UserManager]
import UserService._
def receive = {
case Register(email, password) =>
userManager
}
}
object UserService {
case class Register(email: String, password: String)
}
class ReceptionistService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = injectActorRef [UserService]
def receive = ???
}
Please note, that injectActorRef
creates and actor within the context of current actor. So the equivalent would
be:
val userManager = context.actorOf(injectActorProps[UserService])
Now you need to create binding for the ActorSystem
(it's optional, and if you are using Play, you probably
need to get ActorSystem
from the play application, which already has one), services (which are actors in your case)
and managers:
implicit val module = new Module {
bind [ActorSystem] to ActorSystem("MySystem")
binding toProvider new UserService
binding toProvider new ReceptionistService
bind [UserManager] to new UserManagerImpl
binding to new ProfileManager
}
It is important to bind Actor
s with toProvider
. This will make sure, that each time Akka asks Scaldi for some
particular Actor
, it will always get the new instance of it.
Now, if you want ReceptionistService
to be your top-level actor, you can use it like this:
implicit val system = inject [ActorSystem]
val receptionist = injectActorRef [ReceptionistService]
receptionist ! DoStuff
In this case, system
s guardian actor would be it's supervisor.