$30 off During Our Annual Pro Sale. View Details »

運用を続けていくための Scala の書き方 / scala.rookies#1

kenchan0130
November 29, 2018

運用を続けていくための Scala の書き方 / scala.rookies#1

scala.rookies#1 (https://scala-rookies.connpass.com/event/105904/) で発表した資料です

kenchan0130

November 29, 2018
Tweet

More Decks by kenchan0130

Other Decks in Technology

Transcript

  1. 麊欽׾竲ֽגְֻ׋׭ך
    4DBMBך剅ֹ倯
    2018/11/29ɹscala.rookies#1

    View Slide

  2. 㣐銮姻䛾 (Tadayuki Onishi)
    Software Engineer
    Twitter: @kenchan0130
    Blog: kenchan0130.github.io
    荈䊹稱➜

    View Slide

  3. 痥♧珏ꆃ輐㉀ㅷ《䒷噟罏䎃։
    䎃剢ח鏣甧
    䖞噟㆞秈せ䎃剢植㖈
    ؿؓٔؔIUUQTGPMJPTFDDPNך؟٦ؽأ׾䲿⣘

    View Slide

  4. View Slide

  5. '0-*0דך4DBMB
    5ISJGU"1*4FSWFS"QQMJDBUJPO
    )5513&45"1*4FSWFS
    "QQMJDBUJPO
    #BUDI"QQMJDBUJPO
    1VC4VC欽%FNPO"QQMJDBUJPO

    View Slide

  6. '0-*0דך4DBMB
    4DBMB੡ͷϚΠΫϩαʔϏε







    䎃剢Ĕ晛ٔٔ٦أ
    䎃剢劤ٔٔ٦أ


    View Slide

  7. '0-*0דך4DBMB
    ࣾ಺ϥΠϒϥϦ͸ 20 Ҏ্

    View Slide

  8. '0-*0דך4DBMB
    圫ղז044ٓ؎ـٓٔח״׏ג
    佄ִ׵׸גְ׷

    View Slide

  9. '0-*0ה4DBMB؝ىُصذ؍הךꟼ׻׶

    View Slide

  10. ➙傈鑧ֿׅה
    '0-*0ד㹋ꥷ麊欽׃ג׫גծ
    麊欽את׶ًٝذشٝأ䚍ךぢ♳װغؚך幾㼰׃װְׅ
    ֮׷玎䏝ך倯ꆙָ鋅ִגֹת׃׋կ
    ׉ֿד猘⦐➂ה׃גך剅ֹ倯ך倯ꆙ׾稱➜׃גְֹתׅկ

    View Slide

  11. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁ
    זְ
    剅ֹ倯ך倯ꆙ

    View Slide

  12. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁ
    זְ
    剅ֹ倯ך倯ꆙ

    View Slide

  13. def hoge(): EitherT[Future, FugaException, Unit]
    def bar(): OptionT[Future, Entity]
    ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    def hoge(): Future[Either[FugaException, Unit]]
    def bar(): Future[Option[Entity]]
    ྑ͘ͳ͍ͱߟ͍͑ͯΔॻ͖ํ
    ྑ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  14. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    ٌشسزٓٝأؿؓ٦و٦ָ䗳銲דזְㄎן⳿׃⯋ד׮ꆤ
    ׵׸ג♶欽䠐חٌشسزٓٝأؿؓ٦و٦ָ⢪׻׸ג׷
    穠卓涸חꟼ侧ꟼ侧׃׋铣׫ב׵ְ؝٦سחז׶ָ׍
    䪔ְָ꬗⦜ד׮♧䏝㢌䳔穠卓׾鵤ׅ
    ꟼ侧ⰻ鿇דꟗׄג׷ⴓחכ⢪欽׃ג׮圓׻זְ

    View Slide

  15. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁ
    זְ
    剅ֹ倯ך倯ꆙ

    View Slide

  16. // val ctx: DataBaseContext
    // val userId: UserId
    ctx.transaction {
    val eitherT = for {
    repo1Entity <- OptionT
    (repository1.findEntityBy(userId)).toRight(new RuntimeException("Error"))
    _ <- EitherT.right(repository2.create(repo1Entity))
    } yield ()
    eitherT.run.flatMap {
    case -\/(_) => Future.exception((new RuntimeException("Error")))
    case \/-(a) => Future.value(\/-(a))
    }
    }
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ྑ͘ͳ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  17. implicit class OptionToFuture[A](option: Option[A]) {
    def toFuture(whenNone: => Throwable): Future[A] = option match {
    case Some(a) => Future.value(a)
    case None => Future.exception(whenNone)
    }
    }
    ctx.transaction {
    for {
    repo1EntityOpt <- repository1.findEntityBy(userId)
    repo1Entity <- repo1EntityOpt.toFuture(new RuntimeException("Error"))
    _ <- repository2.create(repo1Entity)
    } yield ()
    }
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ྑ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  18. %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    5SBOTBDUJPOך㢳ֻכ⢽㢩׾ك٦أחٗ٦ٕغحָؙ遤
    ׻׸׷ֿהָ㢳ְ
    稆湫ח⢽㢩׾䫎־׷קֲָ鋅鸐׃ָ״ֻז׷
    -FGUך佝縧ח״׷ٗ٦ٕغحؙ׃䘌׸ָ饯ֿ׷〳腉䚍
    ׾幾׵ׇ׷

    View Slide

  19. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁ
    זְ
    剅ֹ倯ך倯ꆙ

    View Slide

  20. sealed abstract class DbError(val msg: String, e: Throwable) extends RuntimeException(msg, e)
    case class ConnctionError(cause: Throwable) extends DbException("Connection Error", cause)
    case class TimeoutError(cause: Throwable) extends DbException("Timeout Error", cause)
    case class TimeoutError(cause: Throwable) extends DbException("Duplicated Entry Error", cause)
    @Singleton
    class ExecuteImpl {
    def execute(): Future[SchemeModel] = {
    // It is possible the execute method raise some exceptions of DbCtxError.
    ctx.execute().map(Right.apply).rescue {
    case e: DbCtxCancelConnection =>
    Left(ConnctionError(e))
    case e: DbCtxTimeout =>
    Left(TimeoutError(e))
    case e: DbCtxDuplicatedEntry =>
    Left(TimeoutError(e))
    }
    }
    }
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    ྑ͘ͳ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  21. ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    @Singleton
    class ExecuteImpl {
    def execute(): Future[SchemeModel] = ctx.execute()
    }
    @Singleton
    class ExecuteApiController @Inject()(executeImpl: ExecuteImpl) {
    // It is possible the execute method raise some exceptions of DbCtxError.
    def run(): Future[Unit] = executeImpl.execute().map(_ => ())
    }
    @Singleton
    class Endpoint @Inject()(controller: ExecuteApiController) {
    override val executeApi(): Future[Unit] = {
    controller.run().rescue {
    case e: DbCtxError =>
    Future.exception(e)
    }
    }
    }
    ྑ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  22. ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    וֲ׃ג׮3VOUJNFד饯ֿ׏ג׃תֲ׮ךָ֮׷
    %#ך״ֲח帾ְٖ؎َ٦ד饯ֿ׷⢽㢩ך㜥さכ㾴׾ת׋ּ䏝ח
    وحؾָؚٝ䗳銲חז׶撕꧟חז׷
    ،فٔ؛٦ءّٝדعٝسٕ׃׋ְ׮ךח穾׶ծ䖓כ㣐劤ךㄎן⳿
    ׃⩎דٍؗحثׅ׷הأحׅؗٔ׷
    את׶ծ،فٔ؛٦ءّٝך㹋鄲ח꧊⚥דֹ׷

    View Slide

  23. ٌشسزٓٝأؿؓ٦و٦׾ז׷ץֻ㢩鿇ח⳿ׁזְ
    %#זוךزٓٝؠؙءّٝⳢ椚דכ琎噰涸ח⢽㢩׾ちֻ
    ⶰ⡲欽׾搀椚ח㘗ד邌植׃זְ
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁ
    זְ
    剅ֹ倯ך倯ꆙ

    View Slide

  24. // Auto Generated Code
    case class User(id: Int, name: String)
    case class UserDetail(phone: String, birthdate: String, sex: Int)
    // Auto Generated Code
    @Singleton
    class UserInternalApiClient {
    // Auto Generated Code
    def getUser(): Future[User] = { /** compiled code **/ }
    def getUserDetail(userId: Int): Future[UserDetail] = { /** compiled code **/ }
    }
    @Singleton
    class ReturningUserDetailUserCase @Inject()(client: UserInternalApiClient) {
    def run(): Future[UserDetail] = {
    for {
    user <- clinet.getUser()
    userDetail <- client.getUserDetail(user.id)
    } yield userDetail
    }
    }
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁזְ
    ྑ͘ͳ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  25. // Auto Generated Code
    case class User(id: Int, name: String)
    case class UserDetail(birthdate: String, sex: Int)
    // Auto Generated Code
    @Singleton
    class UserInternalApiClient {
    // Auto Generated Code
    def getUser(): Future[User] = { /** compiled code **/ }
    def getUserDetail(userId: Int): Future[UserDetail] = { /**
    compiled code **/ }
    }
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁזְ
    ྑ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  26. case class MyUser(id: UserId, name: Name, birthdate: Birthdate, sex: Sex)
    @Singleton
    class UserInternalApiClientAntiCorruption @Inject()(clinet: UserInternalApiClient) {
    def getUser(): Future[MyUser] = for {
    user <- clinet.getUser()
    detail <- client.getUserDetail(Random.shuffle(idList).head)
    } yield MyUser(
    UserId(user.id),
    Name(user.name),
    Birthdate(detail.birthdate),
    Sex(detail.sex)
    )
    }
    @Singleton
    class ReturningUserDetailUserCase @Inject()(antiCorruption: UserInternalApiClientAntiCorruption) {
    def run(): Future[MyUser] = {
    antiCorruption.getUser()
    }
    }
    %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁזְ
    ྑ͍ͱߟ͍͑ͯΔॻ͖ํ

    View Slide

  27. %#װ"1*ؙٓ؎،ٝزך鵤⽱⦼ך㘗׾׉ךתת⢪ְ㔐ׁזְ
    ⢪ֲ䩛岀ח״׏גכ鵤⽱⦼ָ荈⹛欰䧭ׁ׸ג⤑ⵃ׌ָծ%#װ"1*ؙٓ
    ؎،ٝزזוכ׋׌ךر٦ةا٦أזךדծ䧮ղָ⡲׶׋ְ،فٔ
    ؛٦ءّٝהכⴽ暟
    臰侁꣇姺㾴זו׾䮠׫ծ䧮ղך،فٔ؛٦ءָّٝ欽䠐ׅ׷㘗ח㢌䳔
    ׅ׷ֿהד%#װ"1*ך➬圫㢌刿ח䓼ֻז׷
    侧ָ㢳ְהوحؾָؚٝ꬗⦜׌ָծ㛙暕ז،فٔ؛٦ءّٝח鵚בֻ

    View Slide

  28. '0-*0ה4DBMBךꟼ׻׶׾稱➜
    '0-*0דך麊欽ח״׷濼鋅ַ׵ծ⦐➂涸ח葺ְה
    罋ִ׷倯ꆙ׾稱➜
    תה׭

    View Slide

  29. View Slide

  30. ➰ꐮ

    View Slide

  31. ؙٔ٦ٝ،٦ؗذؙثٍך䱰欽
    ⣛㶷זוךⵖ秈ָ㟓ִծ㹋鄲ָ侔׵ל׵׆ծ䕵ⶴָ僇然חדֹ׷
    ،فٔ؛٦ءّٝךسً؎ٝ׾ؾُ،חדֹ׷
    稢ְַ㹋鄲כ䖓ח׃גِ٦أ؛٦أ׾鎸鶢דֹ׷
    ⡲噟ⴓ䬐ָ֮׷玎䏝דֹ׷
    سً؎ٝ椚鍑ך׋׭ךٙ٦ؙءّحف׾ث٦يדꟚ⪵
    ׉ך➭

    View Slide