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

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

3ae2d821854f9f6749f5ab265559f8ef?s=128

Yoshinobu Wakamatsu

October 26, 2019
Tweet

Transcript

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

    4VNNJU 
  2. 4QFBLFS גࣜձࣾΫϥ΢υϙʔτ एদ ܚ৴ !ZTIOC 普段やっていること • Scalaによるバックエンドも含む開発・運⽤など  4DBMBؔ੢

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

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

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

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

  7.  4DBMBؔ੢ 4VNNJU 

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

    4DBMBؔ੢ 4VNNJU 
  9. ϑΝϯυ ૊੒اۀ ౤ࢿՈ आΓखاۀ ϑΝϯυ΁ग़ࢿ ି෇ ෼഑ ฦࡁ རଉΛؚΊͯ ͓ۚΛฦ͢

    ӡ༻͞Εͨ ͓ۚΛड͚औΔ 'VOETͷ͘͠Έ  4DBMBؔ੢ 4VNNJU 
  10. αʔϏε։ൃͷഎܠ  4DBMBؔ੢ 4VNNJU 

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

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

    
  13. γεςϜΞʔΩςΫνϟ #BDLFOE4FSWJDF 4DBMB DB, Datastore, External REST Services, etc. 4FSWJDFGSPOUFOE

    "ENJOGSPOUFOE 3&45"1* 3&45"1*  4DBMBؔ੢ 4VNNJU 
  14. γεςϜΞʔΩςΫνϟ #BDLFOE4FSWJDF 4DBMB DB, Datastore, External REST Services, etc. 4FSWJDFGSPOUFOE

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

    "ENJOGSPOUFOE 3&45"1* 3&45"1* ڞ௨όοΫΤϯυ αʔϏε υϝΠϯ͕ෳࡶɾີ઀ʹؔ܎͢ΔόοΫΤϯυ͸ ϞϊϦγοΫͳαʔϏεͰߏ੒  4DBMBؔ੢ 4VNNJU 
  16. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻  • αʔόαΠυͰ͸ͳ͘શମͰΤϯδχΞ໊ • ։ൃνʔϜͰϞχλϦϯάͳͲͷӡ༻΋୲౰ • ӡ༻։࢝ޙͷਓతͳӡ༻ෛՙ͕௿͍͜ͱ͕ॏཁ ڽूੑͷߴ͍ϓϩδΣΫτͷํ͕ޮ཰͕Α͍ 

    4DBMBؔ੢ 4VNNJU 
  17. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻  • υϝΠϯͷؔ܎੔ཧ͸೉͍͠ • ޡͬͨίϯςΩετͷ෼ׂ͸ෳࡶ͚ͩ͞ΛߴΊͯ͠·͏ ΞΧ΢ϯτ ϑΝϯυ ސ٬࣋෼ ,:$

    ஫จ ೖग़ۚ υϝΠϯ಺ͷؔ܎ੑ͕੔ཧ͞ΕΔ·Ͱมߋίετͷ௿͍ߏ଄Λ࠾༻͍ͨ͠ σϙδοτ࢒ߴ ࢿۚҠಈ ސ٬ 'VOETʹ͓͚ΔίϯςΩετͷྫ  4DBMBؔ੢ 4VNNJU 
  18. ϞϊϦγοΫͳΞʔΩςΫνϟͷ࠾༻  • ϞϊϦγοΫͳΞʔΩςΫνϟͰ΋มԽʹରԠ͍ͨ͠ • ίϯςΩετͰ͸ͳ͘ந৅౓ΑΔ෼ྨ͸ൺֱతཧղ͠΍͍͢ ΞϓϦέʔγϣϯΞʔΩςΫνϟͱͯ͠$MFBO"SDIJUFDUVSFΛ׆༻  4DBMBؔ੢ 4VNNJU

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

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

  21.  4DBMBؔ੢ 4VNNJU 

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

  23. $MFBO"SDIJUFDUVSFͰఆٛ͞ΕΔϨΠϠʔ ϨΠϠʔ ϨΠϠʔͷ֓ཁ &OUJUZ &OUFSQSJTF#VTJOFTT3VMFT اۀશମͷϏδωεϧʔϧ 6TF$BTF "QQMJDBUJPO#VTJOFTT3VMFT ΞϓϦέʔγϣϯݻ༗ͷϧʔϧɾϩδοΫ *OUFSGBDF"EBQUFST

    ೖग़ྗɾӬଓԽɾ%#ඇґଘͷ42-ͳͲ 'SBNFXPSLT%SJWFST ϑϨʔϜϫʔΫ΍υϥΠόͳͲ  4DBMBؔ੢ 4VNNJU 
  24. %FQFOEFODZ3VMF ಉ৺ԁͷ֎ଆͷϨΠϠʔ͔Β಺ଆͷϨΠϠʔ΁ Ұํ޲ͷґଘʢٯํ޲ͷґଘΛೝΊͳ͍ʣ ç $MFBO"SDIJUFDUVSFʹ͓͚Δॏཁͳίϯηϓτ  4DBMBؔ੢ 4VNNJU 

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

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

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

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

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

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

  31. 'VOETͰ࠾༻͍ͯ͠ΔϨΠϠʔߏ଄ ϨΠϠʔ ϨΠϠʔͷ֓ཁ %PNBJO υϝΠϯݻ༗ͷ֓೦ɾϏδωεϩδοΫ "QQMJDBUJPO ΞϓϦέʔγϣϯݻ༗ͷϩδοΫ *OUFSGBDF ೖग़ྗ΍ͦͷදݱ *OGSBTUSVDUVSF

    ෭࡞༻Λ࣮࣋ͭ૷ ʢ%# 3&454FSWJDF FUD  4DBMBؔ੢ 4VNNJU 
  32. *OGSBTUSVDUVSF *OUFSGBDFT "QQMJDBUJPO %PNBJO ϨΠϠʔͷґଘؔ܎ ্ҐϨΠϠʔ͔Β ԼҐϨΠϠʔͷґଘ͸ೝΊΔ ϨΠϠʔΛ·͙ͨґଘ΋ೝΊΔ ԼҐϨΠϠʔ͔Β্ҐϨΠϠʔͷ ґଘ͸ೝΊͳ͍

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

    ੍ޚͷྲྀΕԼҐ ˠ ্Ґʢৄࡉ΁ͷґଘʣ • ݪଇ௨ΓԼҐϨΠϠʔͰΠϯλʔϑΣΠεΛఆٛɾ࣮૷Λ༩͑Δ  4DBMBؔ੢ 4VNNJU 
  34. ύοέʔδͷશମ૾ ϨΠϠʔͷ ύοέʔδߏ੒ ͦͷଞͷύοέʔδ  4DBMBؔ੢ 4VNNJU 

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

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

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

    4VNNJU 
  38. 1MBZ'SBNFXPSLΛ׆༻͢Δ੍ޚͷྲྀΕ 3FRVFTU.PEFM +40/ 6TF$BTF +40/ 3FTQPOTF.PEFM 2VFSZ $POUSPMMFS "DUJPO JOUFSGBDF

    BQQMJDBUJPO  4DBMBؔ੢ 4VNNJU 
  39. ੍ޚͷྲྀΕ$POUSPMMFS "DUJPO 3FRVFTU.PEFM +40/ 6TF$BTF +40/ 3FTQPOTF.PEFM 2VFSZ $POUSPMMFS "DUJPO

    JOUFSGBDF BQQMJDBUJPO  4DBMBؔ੢ 4VNNJU 
  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 
  41. ੍ޚͷྲྀΕ6TF$BTF 3FRVFTU.PEFM +40/ 6TF$BTF +40/ 3FTQPOTF.PEFM 2VFSZ $POUSPMMFS "DUJPO JOUFSGBDF

    BQQMJDBUJPO  4DBMBؔ੢ 4VNNJU 
  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 
  43. ੍ޚͷྲྀΕ3FTQPOTF.PEFM 3FRVFTU.PEFM +40/ 6TF$BTF +40/ 3FTQPOTF.PEFM 2VFSZ $POUSPMMFS "DUJPO JOUFSGBDF

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

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

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

    7BMVF0CKFDU΋ؚΉʣ • %PNBJO4FSWJDF • 3FQPTJUPSZ  4DBMBؔ੢ 4VNNJU 
  49. 3FQPTJUPSZ • ӬଓԽɾӬଓԽ͞ΕͨσʔλͷࢀরΛఏڙ͢ΔΠϯλʔϑΣΠε • %#͚ͩͰͳ͘3&454FSWJDF $MJFOUͳͲɺ ෭࡞༻Λ൐͏೚ҙͷૢ࡞ͷΠϯλʔϑΣΠεͱͯ͠ఆٛ trait CustomerReadRepository extends

    ReadRepository with CustomerBaseRepository trait CustomerBaseRepository { def findCustomers: Future[Seq[Customer]] }  4DBMBؔ੢ 4VNNJU 
  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 SFBE XSJUFՄೳ SFBEPOMZXSJUBCMFͳΠϯλʔϑΣΠε෼཭  4DBMBؔ੢ 4VNNJU 
  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 
  52. *OGSBTUSVDUVSF࣮૷ͷྫ • %#BDDFTT 4MJDL • 3FEJT • 3&454FSWJDF )551 •

    4.51 • ϚωʔδυαʔϏεʢ"NB[PO424 4 ,.4 FUDʣ  4DBMBؔ੢ 4VNNJU 
  53. *OGSBTUSVDUVSFͷ࣮૷ྫ4MJDL • 'VOETͰ͸%#BDDFTTʹ4MJDLΛ࠾༻ • ͦͷଞͷબ୒ࢶRVJMM 4DBMJLF+%#$ FUD  4DBMBؔ੢ 4VNNJU

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

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

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

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

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

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

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

    ΞϓϦέʔγϣϯʹ͓͚Δ%*  4DBMBؔ੢ 4VNNJU 
  68. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠  4DBMBؔ੢ 4VNNJU