LoginSignup
0
0

More than 5 years have passed since last update.

Akka+AkkaHttp+SlickでTODOアプリAPI(その2)

Last updated at Posted at 2018-04-12

始めに

前回に引き続き、今回はレポジトリ部分を作成します。

構成

  • IntelliJ 2018
  • Scala 2.12.5
  • sbt 1.1.4
  • Akka 2.5.11
  • AkkaHttp 10.1.0
  • Slick 3.2.3
  • Scalaz 7.2.19

成果物

レポジトリ

早速、書いていきます。まずはsrc/main/resources/application.confを作成します。Slickに読ませるデータベース設定です。

application.conf
todo-slick-db {
  dataSourceClass = "slick.jdbc.DriverDataSource"
  connectionPool = disabled
  properties = {
    driver = "org.h2.Driver"
    url = "jdbc:h2:mem:todo-api-todo;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=MySQL;INIT=runscript from 'src/main/resources/create-todo.sql'"
  }
}

合わせて、テーブル定義。

create-todo.sql
CREATE TABLE IF NOT EXISTS todo(
  id INT NOT NULL AUTO_INCREMENT
  , body VARCHAR(255)
  , PRIMARY KEY (id));

ルートパッケージ配下にrepositoryパッケージを追加します。テーブルは前回のHelloSlickとほとんど一緒です。

Model.scala
package todo.api.repository

import slick.jdbc.H2Profile.api._

object Model {

  final case class Todo(id: Int, body: String)

  class TodoTable(tag: Tag) extends Table[Todo](tag, "todo") {

    def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

    def body = column[String]("body")

    override def * = (id, body) <> (Todo.tupled, Todo.unapply)

  }

}

続いてレポジトリ自体を定義しますが、簡易のDIを構成するのでインターフェースと実装は分離します。
また、レポジトリの各メソッドは単純なFutureを返さず、FutureにScalazのEitherを包んで返すことにします。

まずはインターフェースから。

TodoRepository.scala
package todo.api.repository

import scala.concurrent.Future

import scalaz.\/

trait TodoRepository {

  import Model._

  def findAll(): Future[\/[Throwable, Seq[Todo]]]

  def findById(id: Int): Future[\/[Throwable, Option[Todo]]]

  def create(data: Todo): Future[\/[Throwable, Int]]

  def update(data: Todo): Future[\/[Throwable, Int]]

  def delete(id: Int): Future[\/[Throwable, Int]]

}

続けて実装です。今回はslickのfor構文も使ってみます。

TodoRepositoryImpl.scala
package todo.api.repository

import scala.concurrent.Future
import scala.util.control.NonFatal

import scalaz.\/
import scalaz.syntax.ToEitherOps
import slick.dbio.DBIOAction
import slick.jdbc.H2Profile.api._

// Databaseインスタンスをコンストラクタインジェクション
class TodoRepositoryImpl(db: Database) extends TodoRepository with ToEitherOps {

  import Model._

  lazy val todos = TableQuery[TodoTable]

  // Future[R]ではなくFuture[\/[Throwable, R]]を返すようにしたラッパー
  private def run[R](a: DBIOAction[R, NoStream, Nothing]): Future[\/[Throwable, R]] =
    db.run(a).map(_.right[Throwable]).recover {
      case NonFatal(ex) =>
        ex.left[R]
    }

  override def findAll(): Future[\/[Throwable, Seq[Todo]]] = run(todos.result)

  override def findById(id: Int): Future[\/[Throwable, Option[Todo]]] = {
    val q = for {
      r <- todos if r.id === id
    } yield r

    run(q.result.headOption)
  }

  override def create(data: Todo): Future[\/[Throwable, Int]] =
    run(todos returning todos.map(_.id) += data)

  override def update(data: Todo): Future[\/[Throwable, Int]] = {
    val q = for {
      r <- todos if r.id === data.id
    } yield r.body

    run(q.update(data.body))
  }

  override def delete(id: Int): Future[\/[Throwable, Int]] = {
    val q = for {
      r <- todos if r.id === id
    } yield r

    run(q.delete)
  }

}

以上で、レポジトリは完了です。

終わりに

次回は今回作成したレポジトリを扱うアクターを定義します。レポジトリの戻り値をScalazのEitherにした意味がきっと出てくるはず・・・。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0