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

資産運用スタートアップの開発で採用した、PlayによるClean Arcitectureでの設計・開発事例

資産運用スタートアップの開発で採用した、PlayによるClean Arcitectureでの設計・開発事例

Yoshinobu Wakamatsu

October 26, 2019
Tweet

More Decks by Yoshinobu Wakamatsu

Other Decks in Technology

Transcript

  1. ࢿ࢈ӡ༻ελʔτΞοϓͷ։ൃͰ࠾༻ͨ͠
    1MBZʹΑΔ$MFBO"SDIJUFDUVSFͰͷ
    ઃܭɾ։ൃࣄྫ
    גࣜձࣾΫϥ΢υϙʔτ
    एদ ܚ৴
    ˏ4DBMBؔ੢ 4VNNJU
    4DBMBؔ੢ 4VNNJU

    View Slide

  2. 4QFBLFS
    גࣜձࣾΫϥ΢υϙʔτ
    एদ ܚ৴ !ZTIOC

    普段やっていること
    • Scalaによるバックエンドも含む開発・運⽤など
    4DBMBؔ੢ 4VNNJU

    View Slide

  3. 0VUMJOF
    • ࢿ࢈ӡ༻αʔϏεͷ։ൃഎܠ
    • $MFBO"SDIJUFDUVSFͱͦͷղऍ
    • 'VOETͷΞʔΩςΫνϟͱ࣮૷ͷ۩ମྫ
    • ·ͱΊ
    4DBMBؔ੢ 4VNNJU

    View Slide

  4. 0VUMJOF
    • ࢿ࢈ӡ༻αʔϏεͷ։ൃഎܠ
    • $MFBO"SDIJUFDUVSFͱͦͷղऍ
    • 'VOETͷΞʔΩςΫνϟͱ࣮૷ͷ۩ମྫ
    • ·ͱΊ
    4DBMBؔ੢ 4VNNJU

    View Slide

  5. ࠓճࣄྫΛ࿩͢ࢿ࢈ӡ༻αʔϏε
    4DBMBؔ੢ 4VNNJU

    View Slide

  6. 資産形成をしたい個⼈とお⾦を借りたい企業を結ぶ、
    「貸付ファンド」のオンラインマーケット
    第⼆種⾦融商品取引業者のクラウドポートが運営

    View Slide

  7. 4DBMBؔ੢ 4VNNJU

    View Slide

  8. ϑΝϯυ
    ૊੒اۀ
    ౤ࢿՈ आΓखاۀ
    ϑΝϯυ΁ग़ࢿ ି෇
    ͓ۚΛି͢
    お⾦を集める
    'VOETͷ͘͠Έ
    4DBMBؔ੢ 4VNNJU

    View Slide

  9. ϑΝϯυ
    ૊੒اۀ
    ౤ࢿՈ आΓखاۀ
    ϑΝϯυ΁ग़ࢿ ି෇
    ෼഑ ฦࡁ
    རଉΛؚΊͯ
    ͓ۚΛฦ͢
    ӡ༻͞Εͨ
    ͓ۚΛड͚औΔ
    'VOETͷ͘͠Έ
    4DBMBؔ੢ 4VNNJU

    View Slide

  10. αʔϏε։ൃͷഎܠ
    4DBMBؔ੢ 4VNNJU

    View Slide

  11. αʔϏε։ൃͷલఏ৚݅
    • اըʙϩʔϯν·Ͱ൒೥ఔ౓ͷ։ൃظؒʢ౰ॳ༧ఆʣ
    • ۚ༥αʔϏεͳΒͰ͸ͷෳࡶͳυϝΠϯ
    • ূ݊ձࣾɾܾࡁαʔϏεͳͲͱ͸ҟͳΓɺ
    طଘͷۚ༥αʔϏεͱͷ઀ଓ͸ඞཁͳ͍
    4DBMBؔ੢ 4VNNJU

    View Slide

  12. ։ൃ͢ΔνʔϜͷঢ়گ
    • αʔόαΠυͰ͸ͳ͘શମͰΤϯδχΞ໊
    • ։ൃνʔϜͰϞχλϦϯάͳͲͷӡ༻΋୲౰
    • ӡ༻։࢝ޙͷਓతͳӡ༻ෛՙ͕௿͍͜ͱ͕ॏཁ
    4DBMBؔ੢ 4VNNJU

    View Slide

  13. γεςϜΞʔΩςΫνϟ
    #BDLFOE4FSWJDF 4DBMB

    DB, Datastore, External REST Services, etc.
    4FSWJDFGSPOUFOE "ENJOGSPOUFOE
    3&45"1* 3&45"1*
    4DBMBؔ੢ 4VNNJU

    View Slide

  14. γεςϜΞʔΩςΫνϟ
    #BDLFOE4FSWJDF 4DBMB

    DB, Datastore, External REST Services, etc.
    4FSWJDFGSPOUFOE "ENJOGSPOUFOE
    ౤ࢿՈ޲͚
    ΢ΣϒαΠτ
    3&45"1* 3&45"1*
    ࣾ಺޲͚
    ΦϖϨʔγϣϯը໘
    αʔϏε͝ͱʹಠཱɾૄ݁߹ͳϑϩϯτΤϯυ
    4DBMBؔ੢ 4VNNJU

    View Slide

  15. γεςϜΞʔΩςΫνϟ
    #BDLFOE4FSWJDF 4DBMB

    DB, Datastore, External REST Services, etc.
    4FSWJDFGSPOUFOE "ENJOGSPOUFOE
    3&45"1* 3&45"1*
    ڞ௨όοΫΤϯυ
    αʔϏε
    υϝΠϯ͕ෳࡶɾີ઀ʹؔ܎͢ΔόοΫΤϯυ͸
    ϞϊϦγοΫͳαʔϏεͰߏ੒
    4DBMBؔ੢ 4VNNJU

    View Slide

  16. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻

    • αʔόαΠυͰ͸ͳ͘શମͰΤϯδχΞ໊
    • ։ൃνʔϜͰϞχλϦϯάͳͲͷӡ༻΋୲౰
    • ӡ༻։࢝ޙͷਓతͳӡ༻ෛՙ͕௿͍͜ͱ͕ॏཁ
    ڽूੑͷߴ͍ϓϩδΣΫτͷํ͕ޮ཰͕Α͍
    4DBMBؔ੢ 4VNNJU

    View Slide

  17. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻

    • υϝΠϯͷؔ܎੔ཧ͸೉͍͠
    • ޡͬͨίϯςΩετͷ෼ׂ͸ෳࡶ͚ͩ͞ΛߴΊͯ͠·͏
    ΞΧ΢ϯτ
    ϑΝϯυ
    ސ٬࣋෼
    ,:$
    ஫จ
    ೖग़ۚ
    υϝΠϯ಺ͷؔ܎ੑ͕੔ཧ͞ΕΔ·Ͱมߋίετͷ௿͍ߏ଄Λ࠾༻͍ͨ͠
    σϙδοτ࢒ߴ
    ࢿۚҠಈ
    ސ٬
    'VOETʹ͓͚ΔίϯςΩετͷྫ
    4DBMBؔ੢ 4VNNJU

    View Slide

  18. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻

    • ϞϊϦγοΫͳΞʔΩςΫνϟͰ΋มԽʹରԠ͍ͨ͠
    • ίϯςΩετͰ͸ͳ͘ந৅౓ΑΔ෼ྨ͸ൺֱతཧղ͠΍͍͢
    ΞϓϦέʔγϣϯΞʔΩςΫνϟͱͯ͠$MFBO"SDIJUFDUVSFΛ׆༻
    4DBMBؔ੢ 4VNNJU

    View Slide

  19. 0VUMJOF
    • ࢿ࢈ӡ༻αʔϏεͷ։ൃഎܠ
    • $MFBO"SDIJUFDUVSFͱͦͷղऍ
    • 'VOETͷΞʔΩςΫνϟͱ࣮૷ͷ۩ମྫ
    • ·ͱΊ
    4DBMBؔ੢ 4VNNJU

    View Slide

  20. $MFBO"SDIJUFDUVSFͱͦͷղऍ
    4DBMBؔ੢ 4VNNJU

    View Slide

  21. 4DBMBؔ੢ 4VNNJU

    View Slide

  22. ந৅౓ʹΑΔϨΠϠʔͷ෼཭
    ç
    4DBMBؔ੢ 4VNNJU

    View Slide

  23. $MFBO"SDIJUFDUVSFͰఆٛ͞ΕΔϨΠϠʔ
    ϨΠϠʔ ϨΠϠʔͷ֓ཁ
    &OUJUZ &OUFSQSJTF#VTJOFTT3VMFT
    اۀશମͷϏδωεϧʔϧ
    6TF$BTF "QQMJDBUJPO#VTJOFTT3VMFT
    ΞϓϦέʔγϣϯݻ༗ͷϧʔϧɾϩδοΫ
    *OUFSGBDF"EBQUFST ೖग़ྗɾӬଓԽɾ%#ඇґଘͷ42-ͳͲ
    'SBNFXPSLT%SJWFST ϑϨʔϜϫʔΫ΍υϥΠόͳͲ
    4DBMBؔ੢ 4VNNJU

    View Slide

  24. %FQFOEFODZ3VMF
    ಉ৺ԁͷ֎ଆͷϨΠϠʔ͔Β಺ଆͷϨΠϠʔ΁
    Ұํ޲ͷґଘʢٯํ޲ͷґଘΛೝΊͳ͍ʣ
    ç
    $MFBO"SDIJUFDUVSFʹ͓͚Δॏཁͳίϯηϓτ
    4DBMBؔ੢ 4VNNJU

    View Slide

  25. %FQFOEFODZ3VMFͱґଘؔ܎ٯసͷݪଇ
    • ґଘؔ܎ٯసͷݪଇʢ%*1ʣ40-*%ͷݪଇͷͭ
    • ৄࡉ͕ந৅ʹґଘ͢΂͖
    • $MFBO"SDIJUFDUVSFʹ͓͚Δ%FQFOEFODZ3VMF
    • %*1ΛΑΓ۩ମతͳܗͰड़΂ͨ΋ͷ
    4DBMBؔ੢ 4VNNJU

    View Slide

  26. ಉ৺ԁͷ಺ଆΛ௨ա͢Δ੍ޚͷྲྀΕ
    ç
    4DBMBؔ੢ 4VNNJU

    View Slide

  27. ΠϯλʔϑΣΠεʹΑΔ੍ޚͷ൓స
    • ಺ଆ͔Β֎ଆͷϞδϡʔϧΛݺͼग़͢ඞཁ͕͋Δ৔߹ʹ
    ಺ଆͷϨΠϠʔʹΠϯλʔϑΣΠεΛఆٛ
    • ֎ଆͷϨΠϠʔͰ͸಺ଆͰఆٛ͞Εͨ
    ΠϯλʔϑΣΠεʹରͯ͠۩ମత࣮૷Λ༩͑Δ
    4DBMBؔ੢ 4VNNJU

    View Slide

  28. $MFBO"SDIJUFDUVSFͷΤοηϯε
    • ந৅౓ʹԠͯ͡ϨΠϠʔΛ෼཭͢Δ
    • %FQFOEFODZ3VMFͰந৅౓ͷߴ͍ํ޲΁ґଘͤ͞Δ
    • ಺ଆˠ֎ଆͱ͍͏੍ޚͷྲྀΕΛ࣮ݱ͢ΔͨΊʹ
    ΠϯλʔϑΣΠεΛ׆༻͢Δ
    4DBMBؔ੢ 4VNNJU

    View Slide

  29. 0VUMJOF
    • ࢿ࢈ӡ༻αʔϏεͷ։ൃഎܠ
    • $MFBO"SDIJUFDUVSFͱͦͷղऍ
    • 'VOETͷΞʔΩςΫνϟͱ࣮૷ͷ۩ମྫ
    • ·ͱΊ
    4DBMBؔ੢ 4VNNJU

    View Slide

  30. 'VOETʹ͓͚Δ$MFBO"SDIJUFDUVSF
    $MFBO"SDIJUFDUVSFͷΤοηϯεΛ׆༻͠
    ٕज़తͳৄࡉ͔Β෼཭ͨ͠ΞϓϦέʔγϣϯͷઃܭ΁
    4DBMBؔ੢ 4VNNJU

    View Slide

  31. 'VOETͰ࠾༻͍ͯ͠ΔϨΠϠʔߏ଄
    ϨΠϠʔ ϨΠϠʔͷ֓ཁ
    %PNBJO υϝΠϯݻ༗ͷ֓೦ɾϏδωεϩδοΫ
    "QQMJDBUJPO ΞϓϦέʔγϣϯݻ༗ͷϩδοΫ
    *OUFSGBDF ೖग़ྗ΍ͦͷදݱ
    *OGSBTUSVDUVSF
    ෭࡞༻Λ࣮࣋ͭ૷
    ʢ%# 3&454FSWJDF FUD

    4DBMBؔ੢ 4VNNJU

    View Slide

  32. *OGSBTUSVDUVSF
    *OUFSGBDFT
    "QQMJDBUJPO
    %PNBJO
    ϨΠϠʔͷґଘؔ܎
    ্ҐϨΠϠʔ͔Β
    ԼҐϨΠϠʔͷґଘ͸ೝΊΔ
    ϨΠϠʔΛ·͙ͨґଘ΋ೝΊΔ
    ԼҐϨΠϠʔ͔Β্ҐϨΠϠʔͷ
    ґଘ͸ೝΊͳ͍
    4DBMBؔ੢ 4VNNJU

    View Slide

  33. ੍ޚͷྲྀΕͱΠϯλʔϑΣΠε
    • ੍ޚͷྲྀΕ্Ґ ˠ ԼҐʢந৅΁ͷґଘʣ
    • ΠϯλʔϑΣΠεΛ໌ࣔతʹఆٛͤͣ௚઀ݺͼग़͠
    • ࣮૷͔Β෼཭͢ΔԸܙ͕গͳ͍ͱ൑அ
    • ੍ޚͷྲྀΕԼҐ ˠ ্Ґʢৄࡉ΁ͷґଘʣ
    • ݪଇ௨ΓԼҐϨΠϠʔͰΠϯλʔϑΣΠεΛఆٛɾ࣮૷Λ༩͑Δ
    4DBMBؔ੢ 4VNNJU

    View Slide

  34. ύοέʔδͷશମ૾
    ϨΠϠʔͷ
    ύοέʔδߏ੒
    ͦͷଞͷύοέʔδ
    4DBMBؔ੢ 4VNNJU

    View Slide

  35. ࣮૷ύλʔϯͷ۩ମྫ
    • 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    • 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    • ΞϓϦέʔγϣϯʹ͓͚Δ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  36. ࣮૷ύλʔϯͷ۩ମྫ
    • 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    • 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    • ΞϓϦέʔγϣϯʹ͓͚Δ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  37. 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    *OGSBTUSVDUVSF
    *OUFSGBDFT
    "QQMJDBUJPO
    %PNBJO
    1MBZΛ࢖༻͢ΔϨΠϠʔ $POUSPMMFS
    6TF$BTF
    4DBMBؔ੢ 4VNNJU

    View Slide

  38. 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    3FRVFTU.PEFM
    +40/

    6TF$BTF
    +40/

    3FTQPOTF.PEFM
    2VFSZ
    $POUSPMMFS "DUJPO

    JOUFSGBDF
    BQQMJDBUJPO
    4DBMBؔ੢ 4VNNJU

    View Slide

  39. ੍ޚͷྲྀΕ$POUSPMMFS "DUJPO

    3FRVFTU.PEFM
    +40/

    6TF$BTF
    +40/

    3FTQPOTF.PEFM
    2VFSZ
    $POUSPMMFS "DUJPO

    JOUFSGBDF
    BQQMJDBUJPO
    4DBMBؔ੢ 4VNNJU

    View Slide

  40. ੍ޚͷྲྀΕ$POUSPMMFS "DUJPO

    def getList: Action[AnyContent] = asyncSecuredJsonAction(defaultRoles) { implicit request =>
    ...
    usecase.fetchCustomerList.perform(customerCondition).map {
    case (customers, count) =>
    Response(200, ListResponse(count, customers.map(CustomerCardDetailResponseModel.apply)))
    }
    }
    • 1MBZͷ$POUSPMMFS "DUJPO
    Λ࢖༻
    • ೖग़ྗܗࣜͷม׵ͱ6TF$BTFͷݺͼग़͠ʹݶఆ͢Δ࣮૷ϧʔϧ
    4DBMBؔ੢ 4VNNJU

    View Slide

  41. ੍ޚͷྲྀΕ6TF$BTF
    3FRVFTU.PEFM
    +40/

    6TF$BTF
    +40/

    3FTQPOTF.PEFM
    2VFSZ
    $POUSPMMFS "DUJPO

    JOUFSGBDF
    BQQMJDBUJPO
    4DBMBؔ੢ 4VNNJU

    View Slide

  42. ੍ޚͷྲྀΕ6TF$BTF
    case class FetchCustomerList()(
    implicit val customerRepository: CustomerReadRepository,
    implicit val ec: ExecutionContext
    ) extends UseCase {
    def perform(customerCondition: CustomerQueryCondition): Future[(Seq[Customer], Int)] = {
    for {
    customers <- customerRepository.findByCondition(customerCondition)
    count <- customerRepository.countByCondition(customerCondition)
    } yield {
    (customers, count)
    }
    }
    }
    • ΞϓϦέʔγϣϯݻ༗ͷಈ࡞Λ࣮૷
    • 6TF$BTF୯ҐͰΫϥεΛ෼཭
    4DBMBؔ੢ 4VNNJU

    View Slide

  43. ੍ޚͷྲྀΕ3FTQPOTF.PEFM
    3FRVFTU.PEFM
    +40/

    6TF$BTF
    +40/

    3FTQPOTF.PEFM
    2VFSZ
    $POUSPMMFS "DUJPO

    JOUFSGBDF
    BQQMJDBUJPO
    4DBMBؔ੢ 4VNNJU

    View Slide

  44. ੍ޚͷྲྀΕ3FTQPOTF.PEFM
    • ϨεϙϯεͷܕΛDBTFDMBTTͰఆٛ
    • &OUJUZ͋Δ͍͸ͦͷू໿͔Β3FTQPOTF.PEFMΛड͚औΓγϦΞϥΠζ
    case class CustomerStatusResponseModel(
    code: String,
    name: String,
    ) extends ResponseModel
    object CustomerStatusResponseModel {
    import CustomerPresenter._
    def apply(status: CustomerStatus) = {
    new CustomerStatusResponseModel(
    status.code,
    status.name
    )
    }
    }
    4DBMBؔ੢ 4VNNJU

    View Slide

  45. 4XBHHFSͷ࢓༷ͱ࣮૷্ͷܕͷ౷Ұ
    case class CustomerStatusResponseModel(
    code: String,
    name: String,
    ) extends ResponseModel
    object CustomerStatusResponseModel {
    import CustomerPresenter._
    def apply(status: CustomerStatus) = {
    new CustomerStatusResponseModel(
    status.code,
    status.name
    )
    }
    }
    4XBHHFS্Ͱͷఆٛ
    4XBHHFSͰఆٛͨ͠ܕͱΞϓϦέʔγϣϯ্Ͱ࣮૷͢Δํͷ౷Ұ
    ΞϓϦέʔγϣϯ্ͷ࣮૷
    4DBMBؔ੢ 4VNNJU

    View Slide

  46. ࣮૷ύλʔϯͷ۩ମྫ
    • 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    • 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    • ΞϓϦέʔγϣϯʹ͓͚Δ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  47. 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    *OGSBTUSVDUVSF
    *OUFSGBDFT
    "QQMJDBUJPO
    %PNBJO
    %"0 3&45$MJFOU FUD
    3FQPTJUPSZ
    4DBMBؔ੢ 4VNNJU

    View Slide

  48. %PNBJO
    υϝΠϯݻ༗ͷ֓೦ɾ
    ϏδωεϩδοΫΛදݱ͢ΔϨΠϠʔ
    *OGSBTUSVDUVSF
    *OUFSGBDFT
    "QQMJDBUJPO
    %PNBJO
    ओͳߏ੒ཁૉ
    • &OUJUZ 7BMVF0CKFDU΋ؚΉʣ
    • %PNBJO4FSWJDF
    • 3FQPTJUPSZ
    4DBMBؔ੢ 4VNNJU

    View Slide

  49. 3FQPTJUPSZ
    • ӬଓԽɾӬଓԽ͞ΕͨσʔλͷࢀরΛఏڙ͢ΔΠϯλʔϑΣΠε
    • %#͚ͩͰͳ͘3&454FSWJDF $MJFOUͳͲɺ
    ෭࡞༻Λ൐͏೚ҙͷૢ࡞ͷΠϯλʔϑΣΠεͱͯ͠ఆٛ
    trait CustomerReadRepository extends ReadRepository
    with CustomerBaseRepository
    trait CustomerBaseRepository {
    def findCustomers: Future[Seq[Customer]]
    }
    4DBMBؔ੢ 4VNNJU

    View Slide

  50. trait CustomerRepository extends ReadWriteRepository
    with CustomerBaseRepository {
    def save(customer: Customer): Future[ID[Customer]]
    }
    trait CustomerReadRepository extends ReadRepository
    with CustomerBaseRepository
    trait CustomerBaseRepository {
    def findCustomers: Future[Seq[Customer]]
    }
    SFBEPOMZ
    SFBEXSJUFՄೳ
    SFBEPOMZXSJUBCMFͳΠϯλʔϑΣΠε෼཭
    4DBMBؔ੢ 4VNNJU

    View Slide

  51. class FindCustomers(
    implicit
    val customerRepository: CustomerReadRepository,
    val ec: ExecutionContext,
    ) extends UseCase {
    def perform(): Future[Seq[Customer]] = {
    ...
    val customersF = customerRepository.findCustomers
    ...
    }
    }
    ಡΈऔΓઐ༻Ͱ͋Δ͜ͱ͕
    อূ͞Ε͍ͯΔ
    ੍ݶͷ͋Δ࣮૷Λద༻Մೳ
    ʢFHSFBEFSFOEQPJOUͷ࢖༻

    SFBEPOMZXSJUBCMFͳΠϯλʔϑΣΠε෼཭
    4DBMBؔ੢ 4VNNJU

    View Slide

  52. *OGSBTUSVDUVSF࣮૷ͷྫ
    • %#BDDFTT 4MJDL

    • 3FEJT
    • 3&454FSWJDF )551

    • 4.51
    • ϚωʔδυαʔϏεʢ"NB[PO424 4 ,.4 FUDʣ
    4DBMBؔ੢ 4VNNJU

    View Slide

  53. *OGSBTUSVDUVSFͷ࣮૷ྫ4MJDL
    • 'VOETͰ͸%#BDDFTTʹ4MJDLΛ࠾༻
    • ͦͷଞͷબ୒ࢶRVJMM 4DBMJLF+%#$ FUD
    4DBMBؔ੢ 4VNNJU

    View Slide

  54. *OGSBTUSVDUVSFͷ࣮૷ྫ4MJDL
    trait CustomerTable extends ColumnMappers {
    this: Profile =>
    import profile.api._
    class Customers(tag: Tag) extends Table[CustomerRow](tag, "customer") {
    def id = column[ID[Customer]]("id", O.SqlType("INT"), O.PrimaryKey, O.AutoInc)
    def email = column[String]("email", O.SqlType("VARCHAR(256)"))
    def status = column[CustomerStatus]("status", O.SqlType("INT"))
    ...
    type TableRecord = (Option[ID[Customer]], String, CustomerStatus)
    def * = (id.?, email, status) <> (
    (t: TableRecord) => {
    val (id, email, status_) = t
    CustomerRow(id, email, status)
    },
    (customer: CustomerRow) => {
    Option((customer.id, customer.email, customer.status))
    }
    )
    }
    }
    4DBMBؔ੢ 4VNNJU

    View Slide

  55. *OGSBTUSVDUVSFͷ࣮૷ྫ4MJDL
    class CustomerDAO(val dbConfig: DatabaseConfig[JdbcProfile])(implicit val ec: ExecutionContext)
    extends CustomerDAOLike
    with Profile
    with DefaultSlickDBConfig
    trait CustomerDAOLike extends CustomerRepository
    with CustomerReadRepository
    with CustomerTable
    this: Profile with SlickDBConfig[JdbcProfile] =>
    def save(customer: Customer) = {
    val query = customerQuery += customer
    db.run(query.transactionally)
    }
    ...
    }
    4DBMBؔ੢ 4VNNJU

    View Slide

  56. *OGSBTUSVDUVSFͷ࣮૷ྫ3&45 4FSWJDF
    trait CustomerRestServiceLike extends CustomerReadRepository {
    implicit val ec: ExecutionContext
    val client: WSClient
    val baseUrl: String
    lazy val baseUrl = s”..."
    def findCustomers: Future[Seq[Customer]] = {
    val requestUrl = s"$baseUrl/customer"
    for {
    response <- client.url(requestUrl).get()
    } yield {
    val results = for {
    ...
    } yield result
    }
    results.flatten
    }
    }
    }
    4DBMBؔ੢ 4VNNJU

    View Slide

  57. *OGSBTUSVDUVSFͷ࣮૷ͱަ׵Մೳੑ
    • Ͳͷٕज़తৄࡉͰ΋3FQPTJUPSZΛ࣮૷ʢ·ͨ͸࣮૷Մೳʣ
    • ϏδωεϩδοΫΛมߋ͢Δ͜ͱͳٕ͘ज़తৄࡉΛมߋͰ͖Δ
    • ύϑΥʔϚϯεͷվળ
    • 3FQPTJUPSZΛڥքͱͨ͠αʔϏεͷ෼ׂ
    4DBMBؔ੢ 4VNNJU

    View Slide

  58. ࣮૷ύλʔϯͷ۩ମྫ
    • 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    • 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    • ΞϓϦέʔγϣϯʹ͓͚Δ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  59. )PXUP%*
    • 4DBMBʹ͓͚Δબ୒ࢶ͸ (VJDF .BDXJSF FUD
    • 'VOETͰ͸͋͑ͯ*NQMJDJUQBSBNFUFSʹΑΔ%*Λ࢖༻
    4DBMBؔ੢ 4VNNJU

    View Slide

  60. ΞϓϦέʔγϣϯͷॳظԽ
    ֤छΠϯελϯεͷॳظԽͱ
    %*ʹΑΔґଘϞδϡʔϧΛղܾ͢Δ
    ύοέʔδ
    *OUFSGBDFT
    "QQMJDBUJPO
    %PNBJO
    *OGSBTUSVDUVSF
    3VOUJNF
    4DBMBؔ੢ 4VNNJU

    View Slide

  61. *NQMJDJUQBSBNFUFSʹΑΔ%*
    • ܕ҆શੑͷԸܙΛಘΒΕΔ੩త%*Λ࠾༻͍ͨ͠
    • ಉҰͷΠϯλʔϑΣΠεʹෳ਺ͷ࣮૷ΛJOKFDU͍ͨ͠
    • ௨ৗ͸JNQMJDJUQBSBNFUFSͰσϑΥϧτͷ࣮૷ΛJOKFDU
    • ಛఆͷγʔϯͰ͸໌ࣔతʹࢦఆ࣮ͨ͠૷ΛJOKFDU
    4DBMBؔ੢ 4VNNJU

    View Slide

  62. *NQMJDJUQBSBNFUFSʹΑΔ%*
    trait InfrastructureComponents
    extends DatabaseComponents
    with CacheLayerComponents
    with S3Components
    ... {
    this: BuiltInComponentsFromContext =>
    }

    ϨΠϠʔ୯ҐͰͷ$PNQPOFOUʹNJYJO
    trait DatabaseComponents extends SlickComponents {
    this: BuiltInComponentsFromContext =>
    implicit lazy val customerDAO: CustomerRepository = new CustomerDAO(dbConfig)
    implicit lazy val customerReadDAO: CustomerReadRepository = new CustomerDAO(slaveDbConfig)
    ...
    }

    ύοέʔδ͝ͱͷΠϯελϯεॳظԽΛఆٛ
    4DBMBؔ੢ 4VNNJU

    View Slide

  63. *NQMJDJUQBSBNFUFSʹΑΔ%*
    class ApplicationComponents(context: Context)
    extends BuiltInComponentsFromContext(context)
    with I18nComponents
    with HttpFiltersComponents
    with AdminControllerComponents
    with ServiceControllerComponents
    with InfrastructureComponents {
    ...
    }

    ֤ϨΠϠʔͷ$PNQPOFOUTΛ"QQMJDBUJPO$PNQPOFOUT΁NJYJO
    class MainApplicationLoader extends ApplicationLoader {
    def load(context: Context): Application = {
    new ApplicationComponents(context).application
    }
    }

    "QQMJDBUJPO-PBEFSͷఆٛ
    4DBMBؔ੢ 4VNNJU

    View Slide

  64. ಉҰΠϯλʔϑΣΠε্ͷ࣮૷ͷަ׵
    trait DatabaseComponents extends SlickComponents {
    this: BuiltInComponentsFromContext =>
    implicit lazy val customerDAO: CustomerRepository = new CustomerDAO(dbConfig)
    implicit lazy val customerReadDAO: CustomerReadRepository = new CustomerDAO(slaveDbConfig)
    }
    σϑΥϧτͰղܾ͞ΕΔ࣮૷ʢJNQMJDJU

    lazy val adminCustomerComponents = new CustomerComponents {
    import CustomerComponents._
    val fetchCustomerList = FetchCustomerList()
    ...
    }
    *NQMJDJUQBSBNFUFSʹΑΔ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  65. ಉҰΠϯλʔϑΣΠε্ͷ࣮૷ͷަ׵
    ίϯετϥΫλύϥϝʔλͰ࣮૷Λ໌ࣔతʹࢦఆ
    lazy val adminCustomerComponents = new CustomerComponents {
    import CustomerComponents._
    val fetchCustomerList = FetchCustomerList(cachedCustomerDAO, ec)
    ...
    }
    trait CacheLayerComponents {
    this: BuiltInComponentsFromContext with RedisComponents with DatabaseComponents =>
    lazy val cachedCustomerDAO: CusotmerReadRepository =
    new CustomerRedisCache(customerReadDAO)(redisClient, executionContext)
    }
    ҟͳΔ࣮૷ͷྫʢΩϟογϡʣ
    4DBMBؔ੢ 4VNNJU

    View Slide

  66. 0VUMJOF
    • ࢿ࢈ӡ༻αʔϏεͷ։ൃഎܠ
    • $MFBO"SDIJUFDUVSFͱͦͷղऍ
    • 'VOETͷΞʔΩςΫνϟͱ࣮૷ͷ۩ମྫ
    • ·ͱΊ
    4DBMBؔ੢ 4VNNJU

    View Slide

  67. ·ͱΊ
    • αʔϏεͱ$MFBO"SDIJUFDUVSFͷݕ౼എܠʹ͍ͭͯ
    • ΞϓϦέʔγϣϯͱ۩ମతͳ࣮૷ύλʔϯͷྫΛ঺հ
    • 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ
    • 3FQPTJUPSZͷઃܭͱ*OGSBTUSVDUVSFͷ࣮૷
    • ΞϓϦέʔγϣϯʹ͓͚Δ%*
    4DBMBؔ੢ 4VNNJU

    View Slide

  68. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠
    4DBMBؔ੢ 4VNNJU

    View Slide