Search code examples
scaladependency-injectionplayframeworkslick

What's the point of dependency injection if you still have to pass in an argument?


I'm having a bit of trouble understanding the basic idea of dependency injection. (I'm using Play 2.5 with the play-slick module) Say I have a class Users that needs a database connection.

package models

@Singleton
class Users @Inject() (dbConfigProvider: DatabaseConfigProvider) {

  private val db = dbConfigProvider.get[JdbcProfile].db

  private val users = TableQuery[UserTable]

  private val setupAction = DBIO.seq(users.schema.create)

  private val setupFuture: Future[Unit] = db.run(setupAction)

  def getAll(): Future[Seq[User]] = setupFuture.flatMap(_ =>
    db.run(users.result)
  )

  // More methods like the previous
}

When I have a view that needs to access these methods, I would expect the dependency injection system to fill out dbConfigProvider dependency for me, like this.

package views

class UserSearch {

  def index(implicit ec: ExecutionContext): Future[String] = Future(
    (new Users).getAll().map(seq => seq.map(user => user.name).mkString(" "))
  )

}

However this gives me a compilation error and I am forced to make dbConfigProvider a dependency of my view and pass it in explicitly. In this case I finally get the dbConfigProvider from a controller calling the view.

package views

class UserSearch @Inject (dbConfigProvider: DatabaseConfigProvider) {

  def index(implicit ec: ExecutionContext): Future[String] = Future(
    (new Users(dbConfigProvider)).getAll().map(seq =>
      seq.map(user => user.name).mkString(" "))
  )

}

I'm assuming I've misunderstood how dependency injection should work.

So my questions are as follows:

  1. What's the point of using the @Inject() keyword in my model Users then?

  2. Is my design patter flawed? I would love to have Users and UserSearch be objects, but then I can't use dependency injection on them.

  3. In case anyone is familiar with Slick, is my getAll() method the propper way to be working with slick? Is this even the correct way to write asynchronous code?


Solution

  • Thanks to @MichaelZajac comments, I changed UserSearch to be declared as so:

    class UserSearch @Inject (users: Users)
    

    And I now have my controller set up like this:

    class UsersController @Inject()(userSearch: UserSearch) extends Controller {
    
      def index = Action.async {
        implicit request => userSearch.index().map(Ok(_))
      }
    
    }
    

    This answers my first question directly, and seeing this in action also answers my second question. I'm now getting SQL errors, but at least my project compiles. I later figured out my third question – turns out that there is no reason to create the table scheme, because in my case it is done by play evolutions files, thus I removed the setupFuture.flatMap part, even though it works fine with it, and doesn't do anything stupid like creating the table twice, or whatever else you might need to do on table creation/start-up.