scalaakkaakka-httpspray

Akka Http - multiroutes and simple reflection of code with implicits


I have a simple Main object, which create routes for my two services this way:

object GameApp extends App {

  val config = ConfigFactory.load()
  val host = config.getString("http.host")
  val port = config.getInt("http.port")

  implicit val system = ActorSystem("game-service")
  implicit val materializer = ActorMaterializer()
  implicit val executionContext = system.dispatcher

  val game = new GameRouting
  val rate = new RateRouting
  val routes: Route = game.route ~ rate.route

  Http().bindAndHandle(routes, host, port) map {
    binding => println(s"Server start on ${binding.localAddress}")
  } recover {
    case ex => println(s"Server could not start on ${host}:${port}", ex.getMessage)
  }
}

Now, I would like to refactor this code and move this piece into separate method/class/object etc.:

val game = new GameRouting
val rate = new RateRouting
val routes: Route = game.route ~ rate.route

But this classes (GameRouting and RateRouting) uses implicit in constructors and I cannot simply move this code to separate place. How should I refactor this code to get what I want?

My another question is - routes classes (GameRouting and RateRouting) should be class or case class? This is GameRouting class:

trait Protocols extends SprayJsonSupport with DefaultJsonProtocol {
  implicit val gameFormat = jsonFormat4(Game)
}

class GameRouting(implicit executionContext: ExecutionContext) extends Protocols {

  val gameService = new GameService

  val route: Route = {
    pathPrefix("games") {
      pathEndOrSingleSlash {
        get {
          complete {
            gameService.getGames()
          }
        } ~
          post {
            entity(as[Game]) { gameForCreate =>
              createGame(gameForCreate)
            }
          }
      } ~
        pathPrefix(LongNumber) { id =>
          get {
            complete {
              gameService.getGame(id)
            }
          } ~ put {
            entity(as[Game]) { gameForUpdate =>
              complete {
                gameService.update(gameForUpdate)
              }

            }
          }
        }
    }
  }

  private def createGame(gameForCreate: Game) = {
    onComplete(gameService.createGame(gameForCreate)) {
      case Success(created) => complete(StatusCodes.Created, created)
      case Failure(exception) => complete(StatusCodes.BadRequest, s"An error: $exception")
    }
  }
}

And if I make it case I cannot access field routes from Main object. How to fix this or write it in better way? Maybe there are basic questions, but I've been still learning Scala and Akka since few weeks.


Solution

  • You can just move your implicits to separate object:

    object GameAppImplicits {
      implicit val system = ActorSystem("game-service")
      implicit val materializer = ActorMaterializer()
      implicit val executionContext = system.dispatcher
    }
    

    and then import when needed:

    import yourpackage.GameAppImplicits._
    

    For your second question, case classes are usually used for modeling immutable data. In this case you don't really need any feature, that case classes give you (like automatic toString, equals, copy etc.), so I would say it would be best to just use plain class.