Pro Yearly is on sale from $80 to $50! »

Backlogが一体いつから Scalaを遣っていないと 錯覚していた? / Scala Fukuoka 2017 Backlog is using Scala

Backlogが一体いつから Scalaを遣っていないと 錯覚していた? / Scala Fukuoka 2017 Backlog is using Scala

Scala福岡2017 にて、ヌーラボの谷本、松本がお話しした資料です。プロジェクト管理ツールBacklogにおけるScala + Play Frameworkへの移行プロジェクト(通称Play化)について。

3e77f9dbec6a87756d1dbdddab283aee?s=128

Nulab Inc.

July 31, 2017
Tweet

Transcript

  1. #BDLMPH͕Ұମ͍͔ͭΒ 4DBMBΛݣ͍ͬͯͳ͍ͱ ࡨ͍֮ͯͨ͠ʁ d+BWB͔Β4DBMB΁ͷҠߦd ୩ຊཅհ দຊ༟ೋ גࣜձࣾψʔϥϘ 4DBMB෱Ԭ 

  2. None
  3. w ೥ϕʔλެ։ɺ೥ਖ਼ࣜϦϦʔε ͓͔͛͞·Ͱ೥Ҏ্  w ϓϩδΣΫτ؅ཧπʔϧ w λεΫ؅ཧ 8JLJ (JU47/

    ϑΝΠϧڞ༗ w ༗ྉεϖʔε਺Ҏ্ Ϣʔβ਺໿ສਓ
  4. ࠓ೔ͷ࿩͸ɺ ϓϩδΣΫτ؅ཧπʔϧ#BDLMPHʹ͓ ͚Δ4DBMB 1MBZ'SBNFXPSL΁ ͷҠߦϓϩδΣΫτ ௨শ1MBZԽ  ʹ͍ͭͯ

  5. "HFOEB 1MBZԽϓϩδΣΫτͱ͸ ͳͥҠߦ͢Δͷʁ ͳͥ4DBMB 1MBZ'SBNFXPSLʁ Ҡߦํ๏ Ҡߦํ๏Λ࣮ફͯ͠ײͨ͜͡ͱ 4DBMBͷ͓ษڧͷ࿩ #BDLMPHΛ4DBMBʹҠߦ͍ͤͯ͘͞࿩

  6. 1MBZԽϓϩδΣΫτͱ͸

  7. 1MBZԽϓϩδΣΫτͱ͸ #BDLMPHͷ8FCΞϓϦέʔγϣϯ෦෼Λ +BWB 4FBTBS࣮૷͔Β4DBMB 1MBZ 'SBNFXPSL࣮૷ʹॻ͖௚͢ϓϩδΣΫτ ΞΫγϣϯ਺͸d ࠓ·ͰͷλεΫফԽྔΛجʹͨ͠༧ଌͰ͸ޙ೥ ͘Β͍͔͔Δ

  8. ͨͩ͠ɺ ػೳ௥Ճͱόάमਖ਼͸௨ৗ௨Γ ʢ1MBZԽͱ͸ผνʔϜʣ

  9. ͭ·Γɺ ࣮ӡ༻͠ͳ͕Βɺ௕ظ͔͚ؒͯ Ϣʔβ͔Βݟ͑ͳ͍࣮૷Λ͝ ͦͬͱೖΕସ͑ΔϓϩδΣΫτ

  10. ͪͳΈʹ#BDLMPHνʔϜ಺༻ޠͰ͸ʜ 5PNDBU൛ PS+BWB൛  +BWB 4FBTBS࣮૷ͷ#BDLMPH 1MBZ൛ 4DBMB 1MBZ'SBNFXPSL࣮૷ͷ#BDLMPH

  11. ͳͥҠߦ͢Δͷʁ

  12. ཧ༝͸ɺ #BDLMPHͷ੒௕Λ Ճ଎ͤ͞ΔͨΊ

  13. ੒௕ΛՃ଎ͤ͞ΔͨΊʹʜ ҠߦλεΫʹҎԼΛؚΊͨ w 4FBTBS͔Βଔۀ w ϦϑΝΫλϦϯά

  14. 4FBTBS͔Βͷଔۀ w ࢖͍ͬͯΔ4FBTBSϓϩδΣΫτͷϓϩμ Ϋτ͸&0-ʹͳͬͨ ˠ࢖͍ଓ͚Δͱίετ͕ߴ͍ w ΑΓϞμϯͳϓϩμΫτɾϑϨʔϜϫʔΫ ʹ৐Γ׵͑Δ

  15. ϦϑΝΫλϦϯά w ೥Ҏ্ͷྺ࢙ʹ͓͚Δ਺ଟͷमਖ਼ ͰෳࡶԽͨ͠ίʔυͷϝϯςφϯεੑ ޲্

  16. ͳͥ 4DBMB 1MBZ'SBNFXPSLʁ

  17. 4DBMB +BWBΑΓϞμϯͳݴޠ w FY ܕਪ࿦ɺύλʔϯϚον ੩తܕ෇͚ݴޠ w #BDLMPH։ൃऀ͸੩తܕ෇͚ݴޠͷํ ͕ಘҙͳਓ͕ଟ͍

  18. +7.ݴޠ طଘ஌͔ࣝΒେ͖͘཭Εͳ͍ +7.ݴޠؒ͸૬ޓݺͼग़͕͠Մೳ Ұ෦͸طଘίʔυͷ··ʹͯ͠4DBMB͔Β +BWBΛݺͼग़͍ͯ͠Δ

  19. #BDLMPH"1*WͰͷ࣮੷ "1*W͸4DBMB 1MBZ'SBNFXPSLΛ࢖ͬ ͯɺ͔ͭɺ%%%Ͱઃܭ %PNBJO૚͸ڞ௨Ͱ࢖͑Δ "1*͸8FCʹൺ΂ͯͰ͖Δ͜ͱ͕গͳ͍ͷ Ͱ௥Ճ࣮૷͸ඞཁ

  20. Ҡߦํ๏ ϘπҊ

  21. Ҡߦํ๏ ϘπҊ ͢΂ͯͷΞΫγϣϯͷҠ২͕׬͔ྃͯ͠ ΒɺೖΕସ͑Δ 5PNDBU൛ 1MBZ൛ ʙ99೥99݄99೔ 99೥99݄99೔ʙ

  22. Ҡߦํ๏ ϘπҊ ͢΂ͯͷΞΫγϣϯͷҠ২͕׬͔ྃͯ͠ ΒɺೖΕସ͑Δ 5PNDBU൛ 1MBZ൛ ʙ99೥99݄99೔ 99೥99݄99೔ʙ

  23. Ҡߦํ๏ ϘπҊ 5PNDBU൛ͱ1MBZ൛ͷ྆ํʹػೳ௥Ճͱόά मਖ਼͕ඞཁ 1MBZ൛͸௕ظؒϦϦʔε͞Εͳ͍ɺ͔ͭɺ ྆ํͷυοάϑʔσΟϯά͸೉͍͠  όάʹؾ͖ͮͮΒ͍ Ϣʔβ΁ͷӨڹ͕Ͱ΍͍͢

  24. Ҡߦํ๏ ϘπҊ

  25. Ҡߦํ๏ ϘπҊ +BWB͔Βݺͼग़͞ΕΔϝιουΛগͣͭ͠4DBMBʹҠ২ ˎˎˎαʔϏε +BWB *TTVFαʔϏε +BWB ˎˎˎΞΫγϣϯ +BWB ՝୊ฤूΞΫγϣϯ

    +BWB ˎˎˎΞΫγϣϯ +BWB *TTVFαʔϏε 4DBMB ˎˎˎαʔϏε +BWB ˎˎˎαʔϏε +BWB 3%#Πϯϑϥ૚
  26. ͏·͍͖ͦ͘͏ʹݟ͑Δ

  27. Ҡߦํ๏ ϘπҊ ࣮ࡍʹ͸ɺ+BWBͱ4DBMBؒͰ&OUJUZͷ૬ޓม ׵͢Δίʔυ͕ඞཁ º&OUJUZͷ਺ ՝୊ฤूΞΫγϣϯ +BWB *TTVFαʔϏε 4DBMB 3%#Πϯϑϥ૚

    *TTVF&OUJUZ +BWB *TTVF&OUJUZ 4DBMB
  28. ଞʹ΋ઃܭͷࠩҟΛຒΊΔίʔυ΋ඞཁ 4DBMBଆ͸%%%͕ͩɺ+BWBଆ͸%%%Ͱ͸ͳ͍

  29. ׬ྃ࣌ʹ͸ࣺͯΔͱΘ͔͍ͬͯΔίʔ υΛؤுͬͯॻ͘ͷ͸ͭΒ͍ʜ

  30. ࠾༻ͨ͠Ҡߦํ๏

  31. ࠾༻ͨ͠Ҡߦํ๏ ػೳ୯ҐΛ࡞Γ੾ͬͯɺ/HJOYͰΞΫηεΛ 1MBZ൛ʹৼΓ෼͚Δ *TTVFαʔϏε +BWB ՝୊ฤूΞΫγϣϯ +BWB ˎˎˎαʔϏε +BWB 3%#Πϯϑϥ૚

    /HJOY *TTVFαʔϏε 4DBMB ՝୊ฤूΞΫγϣϯ 4DBMB ˎˎˎαʔϏε 4DBMB ˎˎˎΞΫγϣϯ +BWB 5PNDBU൛ 1MBZ൛
  32. ࠾༻ͨ͠Ҡߦํ๏ *TTVFαʔϏε +BWB ՝୊ฤूΞΫγϣϯ +BWB ˎˎˎαʔϏε +BWB 3%#Πϯϑϥ૚ /HJOY *TTVFαʔϏε

    4DBMB ՝୊ฤूΞΫγϣϯ 4DBMB ˎˎˎαʔϏε 4DBMB ˎˎˎΞΫγϣϯ +BWB ͨͩ͠ɺ4FTTJPOΛڞ༗͢Δ࢓૊Έ͕ඞཁ ˠ3FEJTΛ࢖࣮ͬͯ૷ 5PNDBU൛ 1MBZ൛
  33. ࠾༻ཧ༝

  34. ࠾༻ཧ༝ ϘπҊʹ͍͕ۙɺυοάϑʔσΟϯά΋͠ ΍͘͢Ϣʔβ΁ͷӨڹ͸཈͑΍͍͢ ׬ྃޙʹࣺͯΔίʔυ͸΄΅ͳ͍ ίʔυϕʔε͕׬શʹΘ͔ΕΔͨΊɺ 5PNDBU൛ͷઃܭʹҾ͖ͮΒΕΔ͜ͱ΋ͳ͍

  35. ͪͳΈʹ+4$44͸

  36. +4$44 %0.ߏ଄ͷҠ২͕ਖ਼͘͠Ͱ͖͍ͯΕ͹ɺ΄ ΅मਖ਼͸ඞཁͳ͍ ਖ਼͘͠Ҡ২͢ΔͨΊʹͭπʔϧΛ࡞੒

  37. چ7JFXςϯϓϨʔτ͔Β5XJSM΁ͷม׵πʔϧ ׬ᘳͳม׵πʔϧͰ͸ͳ͘ɺਖ਼نදݱΛ༻ ͍ͨิॿతͳ΋ͷ ແବͳख࡞ۀ͕ݮͬͯศར

  38. 5PNDBU൛ͱ1MBZ൛ͷ྆ํʹΞΫηεͯ͠ %0.Λൺֱ͢Δπʔϧ %0.Λύʔεͯ͠ݫີʹൺֱ %0.ཁૉ͕׬શʹҰக͢ΔΘ͚Ͱ͸ͳ͍ͷ Ͱɺ$*ʹ૊ΈࠐΊͳͯ͘ʜ ࢖͍ͬͯΔͷ͸࡞ͬͨຊਓͷΈ ʁ

  39. Ҡߦํ๏Λ࣮ફͯ͠ ײͨ͜͡ͱ

  40. %BTICPBSE͸͜ͷҠߦํ๏Λ࢖ͬͯϦϦʔεࡁΈ

  41. ར఺

  42. ར఺ 5PNDBU൛ͷϦϦʔεͱ1MBZ൛ͷϦϦʔεΛ ੾Γ཭ͯ͠ߟ͑ΒΕΔ Ҡ২͕ਖ਼͘͠Ͱ͖͍ͯͳͯ͘΋/HJOYͷઃ ఆΛ໭ͤ͹͍͍ ؆୯ʹ໭ͤΔͷ͸ਫ਼ਆతʹΑ͍ %BTICPBSEͷϦϦʔε͸౓໭ͨ͠

  43. ՝୊

  44. ՝୊ 1MBZԽνʔϜͷҠ২ͱผνʔϜͷػೳ௥Ճ ͱ͕ฒߦͯ͠ಈ͍͍ͯΔը໘͸ࠩҟ͕Ͱ΍ ͍͢ ίʔυϕʔε͕Θ͔Ε͍ͯΔͨΊίϯϑϦ Ϋτ͠ͳ͍෼ɺؾ͖ͮʹ͍͘ िҰͷϛʔςΟϯάͰҠ২தͷը໘Λผνʔ Ϝʹڞ༗ͯܰ͠ݮ

  45. ՝୊ શը໘ʹٴͿมߋ͸5PNDBU൛ͱ1MBZ൛ͷ྆ ํʹมߋ͕ඞཁͰίετ͕ߴ͍ FY άϩʔόϧόʔͱ͔ ࠓͷͱ͜Ζɺૣ͘ऴΘΒͤΔ͘Β͍͔͠ղ ܾࡦ͸ͳͦ͞͏ʜ

  46. 4DBMBॳ৺ऀ͕ +BWBˠ4DBMB΁ͷҠߦΛͨ͠ ࿩

  47. 4DBMBͷ͓ษڧͷ࿩

  48. 4DBMBͷษڧํ๏ ࣾ಺ษڧձΛ͢Δ ৽ೖࣾһݚमͰ࢖͏ ࣮ۀ຿Ͱ࢖͏

  49. ࣾ಺ษڧձ υϫϯΰͷ4DBMBݚमςΩετΛिҰͰྠߨ

  50. αϧͰ΋Θ͔Δ4DBMB ψʔϥϘຊִࣾि݄༵։࠵

  51. ৽ೖࣾһݚमͰ࢖͏ ࣾ಺޲͚πʔϧΛ4DBMB 1MBZ "LLB 84Ͱ࡞ͬͨ

  52. ࣮ۀ຿Ͱ࢖͏ #BDLMPHͷ"1*͸طʹ4DBMB 1MBZͰಈ࡞͠ ͍ͯΔ

  53. ࣮ࡍͷ͸·ΓͲ͜Ζ ଞͷݴޠͱͷࡉ͔͍จ๏ͷҧ͍ [T] ⟷ <T> => ⟷ -> Option ⟷

    Optional val & var ⟷ let & var JNQMJDJUDMBTT forจ
  54. JNQMJDJUDMBTT implicit class Foo(val a: String) { def foo: Int

    = 3 * a.toInt } ”9”.foo extension String { var foo: Int { return 3 * Int(self)! } } ”9”.foo struct Foo { Foo(const char* a) : a(a) {} int foo() { return 3 * atoi(a); } private: const char* a; }; Foo a = ”33"; a.foo(); $
  55. GPSจϜζΧγΠωʜ forDPNQSFIFOTJPO forMPPQ for { i <- 1 until n

    j <- 1 until i if isPrime(i + j) } yield (i, j)
  56. GPSDPNQSFIFOTJPO IUUQTTQFBLFSEFDLDPNEBJLTZTDBMBGVLVPLB TMJEF ˠ0QUJPOܕΛ࢖͍͜ͳͯ͠ॳ৺ऀ͔Βதڃऀ΁ 4DBMB෱Ԭ for(p <- e) yield e′

    e.map{case p => e′ } — The Scala Language Specification Version 2.9 / 6.19 For Comprehensions and For Loops for(p <- e; p′ <- e′;…) yield e′′ e.flatMap{ case p => for(p′ <- e′;…) yield e′′ } p <- e if g p <- e.withFilter((x1 ,…,xn ) => g)
  57. GPSDPNQSFIFOTJPO&YBNQMF for { i <- 1 until n j <-

    1 until i if isPrime(i + j) } yield (i, j) (1 until n) .flatMap { case i => for (j <- 1 until i if isPrime(i + j)) yield (i, j) } for(p <- e; p′ <- e′;…) yield e′′ e.flatMap{ case p => for(p′ <- e′;…) yield e′′ }
  58. (1 until n) .flatMap { case i => for (j

    <- 1 until i if isPrime(i + j)) yield (i, j) } p <- e if g p <- e.withFilter((x1 ,…,xn ) => g) (1 until n) .flatMap { case i => for (j <- (1 until i) .withFilter { j => isPrime(i + j) } ) yield (i, j) }
  59. (1 until n) .flatMap { case i => for (j

    <- (1 until i) .withFilter { j => isPrime(i + j) } ) yield (i, j) } for(p <- e) yield e′ e.map{case p => e′ } (1 until n) .flatMap { case i => (1 until i) .withFilter { j => isPrime(i + j) } .map { case j => (i, j) } }
  60. GPSDPNQSFIFOTJPO for { i <- 1 until n j <-

    1 until i if isPrime(i + j) } yield (i, j) (1 until n) .flatMap { case i => (1 until i) .withFilter { j => isPrime(i + j) } .map { case j => (i, j) } }
  61. GPSDPNQSFIFOTJPO ࠶ܝ IUUQTTQFBLFSEFDLDPNEBJLTZTDBMBGVLVPLB TMJEF for(p <- e) yield e′ e.map{case

    p => e′ } for(p <- e; p′ <- e′;…) yield e′′ e.flatMap{ case p => for(p′ <- e′;…) yield e′′ } p <- e if g p <- e.withFilter((x1 ,…,xn ) => g) ˠ0QUJPOܕΛ࢖͍͜ͳͯ͠ॳ৺ऀ͔Βதڃऀ΁ 4DBMB෱Ԭ — The Scala Language Specification Version 2.9 / 6.19 For Comprehensions and For Loops
  62. GPSDPNQSFIFOTJPOΛ࢖͏ -JTU 0QUJPO 'VUVSFͳͲ͸ flatMap map withFilterΛ࣮૷͍ͯ͠Δ for { :

    allProjects <- projectRepository.allRelatedProjectsOrderRecent(ctx.user.id) userCount <- userRepository.countUsersOfSpace(ctx.space.id) allProjectIds = allProjects.map(_.id) myPullRequests <- pullRequestRepository.allMyRequest(user.id, allProjectIds) activities <- projectActivityRepository.allGroupedActivitiesOfSpace (allProjectIds, activityTypes, pagination) : } yield DashboardContents( : allProjects = allProjects, userCount = userCount, myPullRequests = myPullRequests,
 activities = activities, : ) }
  63. #BDLMPHΛ 4DBMB1MBZ΁Ҡߦͤ͞Δ࿩

  64. ࢖͍ͬͯΔݴޠ΍ϥΠϒϥϦ ৽ ݱࡏ ݴޠ 4DBMB +BWB ΢Σϒ ϑϨʔϜϫʔΫ 1MBZ 8FCXPSL

    ςϯϓϨʔτ Τϯδϯ 5XJSM 7FMPDJUZ εΫϦϓτ )BYF )BYF
  65. #BDLMPHͷϓϩδΣΫτߏ੒ Core Play API

  66. Java Scala 4DBMB 1MBZ΁ͷҠߦํ਑ 4DBMB൛ͱ+BWB൛ͱΛൺ΂ͯɺϒϥ΢β্Ͱ ϨϯμϦϯά͞Εͨ಺༰͕ɺ%0.ͱͯ͠ಉ ͡ʹͳΔΑ͏ʹ͢Δ +BWB4DSJQU͸ม͑ͳ͍ αʔόʔग़ྗ࣌ͷ%0.ߏ ଄͕ಉ͡ͳΒɺϒϥ΢βͰ

    ͷදࣔ݁Ռ΋ಉ͡ʹͳΔ
  67. 4DBMB 1MBZ΁ͷҠߦखॱ 1MBZ ΞΫγϣϯ͝ͱʹɺ ϧʔςΟϯάઃఆΛॻ͖ม͑Δ ΞΫγϣϯΛ4DBMBʹॻ͖ม͑Δ ςϯϓϨʔτΛॻ͖ม͑Δ Core Play API

  68. ϧʔςΟϯάͷઃఆ xwork-Dashboard.xml <!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd"> <xwork>

    <package name="dashboard" extends="default"> <default-interceptor-ref name=“defaultAuthComponentStack"/> <action name="Dashboard" class="jp.co.nulab.backlog.webwork.dashboard.Dashboard"> <result name="success" type="velocity"> <param name="location">/pages/dashboard/Dashboard.vm</param> </result> </action> </package> </xwork> GET /dashboard controllers.dashboard.DashboardController.viewDashboard() routes
  69. ΞΫγϣϯͷॻ͖͔͑ +BWBͷιʔεΛݟΔ for ifͳ΋ͷΛmap filterͱ͔ʹॻ ͖͔͑Δ def viewDashboard() = addToken

    { actions.userAction.async { implicit request => dashboardApplicationService.viewDashboard().map { contents => Ok(views.html.dashboard.dashboard(contents)) } } }
  70. ςϯϓϨʔτΤϯδϯͷҠߦ  WFMPDJUZUXJSM ࣾ಺πʔϧ Λ࢖͏ ˠجຊతͳஔ׵Λߦ͏͚ͩͳͷͰɺ͍Ζ͍ Ζमਖ਼͢Δ @Html(Messages(“msg.dashboard.storage.warning", contents.diskUsageRatio)) Twirl

    Velocity ${action.getText(‘msg.dashboard.storage.warning', $!webwork.htmlEncode($action.diskUsageRatio))}
  71. ςϯϓϨʔτΤϯδϯͷҠߦ  #parse("/pages/_newUI/common/parts/globalHeader.vm") @import common.parts._ @globalHeader(contents) Twirl Velocity

  72. 4DBMB 1MBZ΁ͷҠߦखॱ $PSF "QQMJDBUJPO4FSWJDFʹϝιουΛੜ΍͢ ˠίϯςϯπΛͭͬͯ͘Ϗϡʔʹ౉͢ $POUFOUT'BDUPSZΛ࡞Δ 3FQPTJUPSZܦ༝ͰσʔλΛऔΓग़͢ 4DBMJLF+%#$Ͱ%#͔ΒσʔλΛऔΔ Core Play

    API
  73. "QQMJDBUJPO4FSWJDF "QQMJDBUJPO4FSWJDFʹϝιουΛੜ΍͢ ˠίϯςϯπΛͭͬͯ͘Ϗϡʔʹ౉͢ def viewDashboard()(implicit ctx: UserContext): Future[DashboardContents] = {

    for { contents <- dashboardContentsFactory.create() } yield contents }
  74. $POUFOUT'BDUPSZ 3FQPTJUPSZܦ༝ͰσʔλΛऔΓग़͢ def create()(implicit ctx: UserContext, io: IOContext, ec: ExecutionContext):

    Future[DashboardContents] = { val user = ctx.user val activityTypes = ActivityType.availableActivities(user.role) val pagination = PaginationQuery(count = 10) for { : allProjects <- projectRepository.allRelatedProjectsOrderRecent(ctx.user.id) userCount <- userRepository.countUsersOfSpace(ctx.space.id) : } yield DashboardContents( : allProjects = allProjects, userCount = userCount, : ) }
  75. ؆ܿʹॻ͚Δ ͢΂ͯͷਓʹಡΈ΍͍͢Θ͚Ͱ͸ͳ͍  w ίϨΫγϣϯ 0QUJPOؚΉ ͷ"1*͕๛෋ w ύλʔϯϚονϯά Α͔ͬͨ͜ͱ

  76. ॻ͖׵͑ʹےྗ͕ཁΔ w ιʔεΛಡΈղ͘ w +BWBͷݹ͍ϥΠϒϥϦ w 4DBMBΒ͘͠ॻ͘ w %%%Β͘͠ॻ͘ Ή͔͍ͣ͠ͱ͜Ζ

    Java Scala
  77. ·ͱΊ

  78. ·ͱΊ #BDLMPHͷ4DBMB 1MBZԽ w ϝϯςφϯεੑΛ޲্ͤ͞ɺ։ൃΛՃ଎ͤ͞Δ w ػೳ୯ҐͰঃʑʹҠߦͤ͞Δ w +4 $44͸΄ͱΜͲ͍͡Βͳ͍

    4DBMBͰͷ࣮ࡍͷػೳ୯ҐͰͷҠߦ w ΞΫγϣϯ͝ͱʹॻ͖׵͍͑ͯ͘
  79. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠