Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践GraphQL on Scala/Real world GraphQL on Scala

petitviolet
November 10, 2018

実践GraphQL on Scala/Real world GraphQL on Scala

Scala関西サミット2018 「実践GraphQL on Scala」の発表資料

- https://github.com/petitviolet/graphql_on_scala

petitviolet

November 10, 2018
Tweet

More Decks by petitviolet

Other Decks in Programming

Transcript

  1. ࣮ફ(SBQI2-PO4DBMB
    4DBMBؔ੢4VNNJU
    !QFUJUWJPMFU


    View Slide

  2. ͸͡Ίʹ
    w αϯϓϧίʔυ
    w IUUQTHJUIVCDPNQFUJUWJPMFUHSBQIRM@PO@TDBMB
    w ϦϯΫू
    w IUUQTHSBQIRMHJUIVCJPMFBSO
    w IUUQTXXXIPXUPHSBQIRMDPN
    w IUUQTTBOHSJBHSBQIRMPSHMFBSO
    2

    View Slide

  3. Ͱɺ୭ʁ
    w খࢵ߂و
    w !QFUJUWJPMFU
    w 'SJOHFגࣜձࣾ
    w ؔ੢ग़਎
    w ڈ೥ͷ4DBMBؔ੢Ͱ͸ϝλϓϩάϥϛϯάʹ͍ͭͯ
    3

    View Slide

  4. (SBQI2-
    w ੝Γ্͕ͬͯΔ͚ͲΑ͘஌Βͳ͍
    w ஌ͬͯΔ͚Ͳ4DBMBͰग़དྷΔͷʁ
    w ΍ͬͯΔ͚Ͳ΍Γํʹࣗ৴ͳ͍
    4

    View Slide

  5. ࠓ೔ͷΰʔϧ
    ʮ࣍ͷϓϩδΣΫτͰ͸
    ɹ(SBQI2-Λ 4DBMBͰ
    ΍Γ͍ͨʂʯ
    5

    View Slide

  6. ࠓ೔࿩͢͜ͱ࿩͞ͳ͍͜ͱ
    ࿩͢͜ͱ
    w (SBQI2-ʹ͍ͭͯ͟ʔͬͱ
    w 4DBMBͰͷ࣮ફʹ͍ͭͯ
    w ͳͥ(SBQI2-ͳͷ͔ɺͲ͏΍͍ͬͯΔͷ͔
    w جຊతͳ࣮૷ͷ࢓ํ͔Βઃܭʹ͍ͭͯ
    ࿩͞ͳ͍͜ͱ
    w 4DBMBҎ֎Ͱͷ(SBQI2-ʹ͍ͭͯ
    w 4VCTDSJQUJPO!
    w ͦͷଞࡉ͔͍࢓༷
    6

    View Slide

  7. ࠓ೔࿩͢͜ͱ
    w (SBQI2-ͱ͸
    w Ͳ͏cͳͥ
    (SBQI2-Λ࣮ફ͍ͯ͠Δͷ͔
    w 4DBMBͰͷ(SBQI2-
    w 4DBMBͰͷ(SBQI2-࣮ફ
    7

    View Slide

  8. ɹ(SBQI2-ʁ
    8

    View Slide

  9. 9
    (SBQI2-JTBOPQFOTPVSDFEBUBRVFSZBOE
    NBOJQVMBUJPOMBOHVBHF BOEBSVOUJNFGPSGVMpMMJOH
    RVFSJFTXJUIFYJTUJOHEBUB
    https://en.wikipedia.org/wiki/GraphQL

    View Slide

  10. (SBQI2-ͱ͸
    w 'BDFCPPL͕೥ࠒʹൃදͨ͠΋ͷ
    w ಺෦తʹ͸೥͘Β͍͔Β։ൃ
    w 2VFSZ-BOHVBHF
    w "1*ͷͨΊͷΫΤϦݴޠ
    w (SBQI2-ͱ͍͏࢓༷͕͋Δ
    w ެࣜIUUQTHSBQIRMPSH
    10

    View Slide

  11. (SBQI2-ͱ͸
    w ࢓༷͸'BDFCPPL͕ࡦఆ͍ͯ͠Δ
    w IUUQTGBDFCPPLHJUIVCJPHSBQIRM
    w IUUQTHJUIVCDPNGBDFCPPLHSBQIRM
    w ࢓༷ͱ͖ͯͬͪ͠ΓͱܾΊΒΕ͍ͯΔ
    w (JUIVCͰΦʔϓϯʹٞ࿦͞Ε͍ͯΔ
    w ֓೦Ͱ͸ͳ͍
    11

    View Slide

  12. (SBQI2-ͱ͸
    w (JUIVC͕ʹ"1*Wͱͯ͠(SBQI2-Λ࠾༻
    w ͔ͦ͜Β஌໊౓্͕͕ͬͨ

    12

    View Slide

  13. 3&45GVMͱൺ΂ͯΈΔ
    w (SBQI2-੝Γ্͕ͬͯΔ
    13

    View Slide

  14. (SBQI2-ͱ͸
    14
    https://gql.foundation/

    View Slide

  15. (SBQI2-ΩςΔ
    15

    View Slide

  16. (SBQI2-ͱ͸
    w ܕγεςϜΛఏڙ͍ͯ͠Δ
    w "1*ΠϯλϑΣʔεͰ࢖༻͢ΔܕΛఆٛͰ͖Δ
    w ඞཁͳσʔλΛΫϥΠΞϯτ͔Β໰͍߹ΘͤΔ
    w ໰͍߹Θͤ ΫΤϦ
    ༻ͷݴޠ࢓༷
    w ඞཁͳσʔλ ϑΟʔϧυ
    ͚ͩΛࢦఆͰ͖Δ
    w (SBQIJ2-͕ඇৗʹศར
    w ϒϥ΢βͰಈ͘*%&తͳ΋ͷ
    16

    View Slide

  17. (SBQI2-ͷ༷ࢠ (SBQIJ2-

    17

    View Slide

  18. (SBQI2-ͱ͸
    w ؔ࿈͢ΔΦϒδΣΫτ΋·ͱΊͯऔಘՄೳ
    w ؔ࿈Λܕͱͯ͠දݱ͓͚ͯ͠͹ྑ͍
    w υΩϡϝϯτ΋εΩʔϚ͔Βࣗಈੜ੒
    18

    View Slide

  19. 3&45GVM (SBQI2-ͱͷൺֱ
    19
    QSPT DPOT
    3&45GVM
    w Α͘஌ΒΕ͍ͯΔ
    w Ϧιʔεͱ"1*ͷؔ࿈෇͚
    w )551ͱ૬ੑ͕ྑ͍
    w Ϧιʔεͷઃܭ͕େม
    w ϦΫΤετճ਺૿͕͑ͪ
    (SBQI2-
    w *'Λܕఆٛग़དྷΔ
    w ϦΫΤετΛ࠷దԽग़དྷΔ
    w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ
    ʹدͤΔ͜ͱ͕ग़དྷΔ
    w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍
    w )551ͱ૬ੑ͕ྑ͘ͳ͍
    w ϑΝΠϧΛѻ͍ਏ͍ FHը૾

    View Slide

  20. (SBQI2-ͷ࢖͍Ͳ͜Ζ͸Ͳ͔͜ ओ؍
    w ࢖͍΍͍͢ͷ͸಺෦޲͚"1*
    w ߟ͑Δ͜ͱ͕গͳ͘ɺγϯϓϧʹอͪ΍͍͢
    w ֎෦޲͚͸·ͩ3&45ͷํ͕Ұൠత͔΋
    w #''ʹ΋ྑͦ͞͏
    w ෳ਺ͷόοΫΤϯυΛଋͶͨ"1*Λ࡞Γ΍͍͢
    w ඞཁͳσʔλΛඞཁͳ࣌ʹऔಘ
    w ෆඞཁͳͷʹຖճ໰͍߹ΘͤΔɺΛආ͚Δ
    20

    View Slide

  21. ஫ҙ఺
    w (SBQI2-͸ศར͕ͩɺۜͷ஄ؙͰ͸ͳ͍
    w Α͘ग़དྷͨ3&45ͳΒɺ(SBQI2-Ͱ࣮ݱ͍ͯ͠Δ
    ͜ͱ͸શͯग़དྷΔ
    w ʮ(SBQI2-ʹ͔͠ग़དྷͳ͍͜ͱʯ͸ແ͍
    w αϘͬͯ΋͋Δఔ౓࣮ݱͰ͖Δͷ͕(SBQI2-
    w "1*ͷܕදݱ υΩϡϝϯτࣗಈੜ੒ͳͲ
    w ౰વࣦΘΕΔ΋ͷ΋͋Δ
    21

    View Slide

  22. (SBQI2-ͷUZQFTZTUFN
    ओͳొ৔ਓ෺͸͜ͷลΓ
    w OVMMBCMFOPOOVMM -JTU
    w 0CKFDU5ZQF
    w *OUFSGBDF
    w 4DBMBS5ZQF
    w &OVN5ZQF
    w 6OJPO5ZQF
    w "SHVNFOU
    w *OQVU0CKFDU5ZQF
    22

    View Slide

  23. (SBQI2-ͷUZQFTZTUFN
    ओͳొ৔ਓ෺͸͜ͷลΓ
    w OVMMBCMFOPOOVMM -JTU
    w 0CKFDU5ZQF
    w *OUFSGBDF
    w 4DBMBS5ZQF
    w &OVN5ZQF
    w 6OJPO5ZQF
    w "SHVNFOU
    w *OQVU0CKFDU5ZQF
    23

    View Slide

  24. OVMMBCMFOPOOVMM -JTU
    w OVMMBCMFOPOOVMM
    w AA͕͍ͭͨΒOPOOVMM
    w 0QUJPO<">A͔A"Aͷҧ͍
    w -JTU
    w <9>Ͱ9ͷ-JTUʹͳΔ
    w <4USJOH>͸OVMMBCMFͳ4USJOHͷOVMMBCMFͳ-JTU
    type User {
    name: String! ←!Ͱnon-null
    projects: [Project!]! ←[x]ͰxͷList
    }
    24
    (SBQI2-ͷܕఆٛ

    View Slide

  25. OVMMBCMFOPOOVMM -JTU
    type User {
    name: String!
    projects: [Project!]!
    }
    25
    case class User(name: String, projects: Seq[Project])
    type User {
    name: String
    projects: [Project]
    }
    case class User(
    name: Option[String], projects: Option[Seq[Option[Project]]])

    View Slide

  26. ࣮ફʁ
    26

    View Slide

  27. Ͳ͏࣮ફ͍ͯ͠Δͷ͔
    w ޿ࠂγεςϜͷ؅ཧը໘޲͚"1*
    w ޿ࠂ഑৴ͷઃఆ΍ϨϙʔτΛӾཡͰ͖Δ
    w ಺෦޲͚"1*ͱͯ͠৴པͰ͖ΔΫϥΠΞϯτʹఏڙ
    w ΫϥΠΞϯτ͸&MNͰॻ͔Ε͍ͯΔ
    w ,VCFSOFUFTʹσϓϩΠ͍ͯ͠Δ
    w 2VFSZͱ.VUBUJPOͲͪΒ΋(SBQI2-
    w ϑΝΠϧͷ\Ξοϓ μ΢ϯ^ϩʔυ͸ඇ(SBQI2-
    27

    View Slide

  28. ͳͥ(SBQI2-Λ࠾༻ͨ͠ͷ͔
    w ؅ཧը໘࡞Δ͚ͲɺͲ͏΍ͬͯ࡞Δʁ
    w Ϧιʔεઃܭ͸೉͍͠ͷ͸ݟ͑ͯΔ
    w ʮ͜ͷը໘։͘ͷʹ"1*Λճ΋ୟ͘ͷʁʯ
    w ʮEPDTͱҧ͏Ϩεϙϯεฦͬͯ͘ΔͰ͚͢Ͳʁʯ
    w 4XBHHFS+40/4DIFNB࢖͏ʜʁ
    w %%%ͷ3FQPTJUPSZ͕pOE#Z9YYͩΒ͚
    w ը໘͔ΒͷϦΫΤετ͕ଟ༷
    28

    View Slide

  29. 3&45GVM (SBQI2-ͱͷൺֱ
    29
    QSPT DPOT
    3&45GVM
    w Α͘஌ΒΕ͍ͯΔ
    w Ϧιʔεͱ"1*ͷؔ࿈෇͚
    w )551ͱ૬ੑ͕ྑ͍
    w Ϧιʔεͷઃܭ͕େม
    w ϦΫΤετճ਺૿͕͑ͪ
    (SBQI2-
    w *'Λܕఆٛग़དྷΔ
    w ϦΫΤετΛ࠷దԽग़དྷΔ
    w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ
    ʹدͤΔ͜ͱ͕ग़དྷΔ
    w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍
    w )551ͱ૬ੑ͕ྑ͘ͳ͍
    w ϑΝΠϧΛѻ͍ਏ͍ FHը૾

    View Slide

  30. 3&45GVM (SBQI2-ͱͷൺֱ
    30
    QSPT DPOT
    3&45GVM
    w Α͘஌ΒΕ͍ͯΔ
    w Ϧιʔεͱ"1*ͷؔ࿈෇͚
    w )551ͱ૬ੑ͕ྑ͍
    w Ϧιʔεͷઃܭ͕େม
    w ϦΫΤετճ਺૿͕͑ͪ
    (SBQI2-
    w *'Λܕఆٛग़དྷΔ
    w ϦΫΤετΛ࠷దԽग़དྷΔ
    w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ
    ʹدͤΔ͜ͱ͕ग़དྷΔ
    w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍
    w )551ͱ૬ੑ͕ྑ͘ͳ͍
    w ϑΝΠϧΛѻ͍ਏ͍ FHը૾
    ਏ͍ͱࢥ͍ͬͯΔͱ͜ΖΛ
    େମղܾͰ͖ͦ͏ͳݟࠐΈ

    View Slide

  31. 3&45GVM (SBQI2-ͱͷൺֱ
    31
    QSPT DPOT
    3&45GVM
    w Α͘஌ΒΕ͍ͯΔ
    w Ϧιʔεͱ"1*ͷؔ࿈෇͚
    w )551ͱ૬ੑ͕ྑ͍
    w Ϧιʔεͷઃܭ͕େม
    w ϦΫΤετճ਺૿͕͑ͪ
    (SBQI2-
    w *'Λܕఆٛग़དྷΔ
    w ϦΫΤετΛ࠷దԽग़དྷΔ
    w ΫΤϦͷ੹຿ΛΫϥΠΞϯτ
    ʹدͤΔ͜ͱ͕ग़དྷΔ
    w ஌ݟࣄྫ͕ཷ·͍ͬͯͳ͍
    w )551ͱ૬ੑ͕ྑ͘ͳ͍
    w ϑΝΠϧΛѻ͍ਏ͍ FHը૾
    ੾Γ୓͍͍֮ͯ͘ޛͰ
    Կͱ͔ͳΓͦ͏

    View Slide

  32. ͳͥ(SBQI2-Λ࠾༻ͨ͠ͷ͔
    w ͋ͱ͸λΠϛϯά
    w ৽͍͠ϓϩμΫτ։ൃͷػձ͕͋ͬͨ
    w (SBQI2-ʹϚονͨ͠ཁ݅ͩͬͨ
    w νʔϜϝϯόʹ΋(SBQI2-΍Γ͍ͨਓ͕͍ͨ
    w ϑϩϯτΤϯυʹ͍ͨͷ͕޾ӡ
    32

    View Slide

  33. (SBQI2-Λ࠾༻ͯ͠Ͳ͏ʁ
    w "1*ઃܭͰ೰Ή͜ͱ͕গͳͯ͘޾ͤ
    w ΫϥΠΞϯτͷ͜ͱΑ͘஌Βͳͯ͘΋Կͱ͔ͳΔ
    w ཪػೳΛఏڙग़དྷ͍ͯΔ
    w (SBQIJ2-͕Ұ࣌తͳ؅ཧը໘ʹ
    w 4DBMBͰඇৗʹ΍Γ΍͍͢
    33

    View Slide

  34. 4DBMBͰͷ(SBQI2-
    34

    View Slide

  35. 4DBMBͰͷ(SBQI2-
    w TBOHSJBHSBQIRMTBOHSJB
    w (SBQI2-ϥΠϒϥϦ!0MFH*MZFOLP
    w 4DBMBͰ(SBQI2-΍ΔͳΒίϨ
    w ಛఆͷ8FC"QQ'SBNFXPSLʹґଘ͠ͳ͍
    w *'͕+40/ʹͳ͍ͬͯΔͷͰࣗ༝ʹબ΂Δ
    w +T7BMVFతͳ΋ͷΛύʔεͯ͠(SBQI2-ΫΤϦ
    ͱ࣮ͯ͠ߦ͠ɺ+T7BMVFతͳ΋ͷΛฦ͢
    35

    View Slide

  36. implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    36
    (SBQI2-ͷܕఆٛ
    4BOHSJBͰͷఆٛ

    View Slide

  37. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    37

    View Slide

  38. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    38
    0CKFDU5ZQFʹ
    ରԠ෇͚Δܕ

    View Slide

  39. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    39
    $POUFYU ޙड़

    View Slide

  40. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    40
    ϑΟʔϧυఆٛ

    View Slide

  41. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    41
    AOBNFAϑΟʔϧυ

    View Slide

  42. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    42
    SFTPMWFؔ਺ͷ$POUFYUʹ͸
    w $POUFYU $UY

    w 7BMVF $IBSBDUFS

    ͕٧·͍ͬͯΔ

    View Slide

  43. type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    43
    $POUFYUWBMVFͰ6TFSܕͷ
    ΠϯελϯεʹΞΫηεՄೳ

    View Slide

  44. implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    0CKFDU5ZQF
    44
    <">Λ-JTU5ZQF "5ZQF
    Ͱදݱ
    OPOOVMMͳ1SPKFDUͷ
    OPOOVMMͳ-JTU

    View Slide

  45. implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    type User {
    id: ID!
    name: String!
    status: UserStatus!
    projects: [Project!]!
    }
    0CKFDU5ZQF
    45
    SFTPMWFؔ਺ͷதͰ%#ΞΫηεͳͲͯ͠
    ؔ࿈ΦϒδΣΫτΛऔಘग़དྷΔ

    View Slide

  46. implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    46
    6TFSOBNFΛऔಘ͢Δ2VFSZΛॻ͘ͱɺ
    VTFS5ZQFͷOBNFϑΟʔϧυͷSFTPMWFͷ݁ՌΛಘΔ

    View Slide

  47. implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )
    0CKFDU5ZQF
    47
    %#ΞΫηε͕ඞཁʹͳΔϑΟʔϧυ͕͋ͬͯ΋
    ͦͷϑΟʔϧυΛऔಘ͠ͳ͍ͳΒSFTPMWFؔ਺͕࣮ߦ͞
    Εͣ%#ΞΫηε΋ൃੜ͠ͳ͍

    View Slide

  48. 0CKFDU5ZQF
    w ఆ͕ٛΊΜͲ͍͘͞ʁ
    48
    implicit val userType: ObjectType[Ctx, User] =
    ObjectType(name = "User",
    fields = fields[Ctx, User](
    Field("id", IDType,
    resolve = { ctx => ctx.value.id }),
    Field("name", StringType,
    resolve = { ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType,
    resolve = { ctx => ctx.value.status }),
    Field("projects", ListType(projectType),
    resolve = { ctx => ProjectResolver.byUser(ctx.value) })
    )
    )

    View Slide

  49. 0CKFDU5ZQF
    w ͦ͜ͰϚΫϩ
    w DBTFDMBTTͳΒ͍͍ײ͡ʹղܾͯ͘͠ΕΔ
    w ґଘ͢Δଞͷಠࣗܕ͸JNQMJDJUͰએݴ͓ͯ͘͜͠ͱ
    w ࠓճͳΒQSPKFDU5ZQFͱ͔
    import sangria.macros.derive
    val userType: ObjectType[Ctx, User] =
    derive.deriveObjectType()
    49

    View Slide

  50. &OVN5ZQF
    val userStatusType = EnumType[UserStatus](
    "UserStatus",
    values = List(
    EnumValue[UserStatus]("Active", value = UserStatus.Active),
    EnumValue[UserStatus]("Paused", value = UserStatus.Paused)
    )
    )
    enum UserStatus {
    Active
    Paused
    }
    sealed abstract class UserStatus(val value: Int)
    object UserStatus {
    case object Active extends UserStatus(1)
    case object Paused extends UserStatus(2)
    }
    50

    View Slide

  51. &OVN5ZQF΋ϚΫϩͰ
    val userStatusType: EnumType[UserStatus] =
    derive.deriveEnumType[UserStatus]()
    enum UserStatus {
    Active
    Paused
    }
    sealed abstract class UserStatus(val value: Int)
    object UserStatus {
    case object Active extends UserStatus(1)
    case object Paused extends UserStatus(2)
    }
    51

    View Slide

  52. w &OVN5ZQF΋ϚΫϩͰಋग़Ͱ͖Δ
    w ATFBMFEAͱAPCKFDUAͷ૊Έ߹Θͤ
    &OVN5ZQF΋ϚΫϩͰ
    val userStatusType: EnumType[UserStatus] =
    derive.deriveEnumType[UserStatus]()
    sealed abstract class UserStatus(val value: Int)
    object UserStatus {
    case object Active extends UserStatus(1)
    case object Paused extends UserStatus(2)
    }
    52

    View Slide

  53. w ީิʹͳΔܕ 0CKFDU5ZQF
    Λ-JTUͰ༩͑Δ
    6OJPO5ZQF
    union Plan = Free | Standard | Enterprise
    object Plan {
    case class Free(startDate: ZonedDateTime)
    case class Standard(startDate: ZonedDateTime)
    case class Enterprise(userLimit: Int, startDate: ZonedDateTime)
    }
    implicit val planType: UnionType[Ctx] = {
    val free = derive.deriveObjectType[Ctx, Plan.Free]()
    val standard = derive.deriveObjectType[Ctx, Plan.Standard]()
    val enterprise = derive.deriveObjectType[Ctx, Plan.Enterprise]()
    UnionType("Plan", types = free :: standard :: enterprise :: Nil)
    }
    53

    View Slide

  54. w ೖྗ஋ͱͯ͠+40/Λड͚औΔͨΊͷ΍ͭ
    w +40/Ͱड͚औͬͯ4DBMBͷΦϒδΣΫτʹม׵
    *OQVU0CKFDU5ZQF
    mutation {
    UpdateTask(attributes: {
    taskId: "t1",
    taskName: "newName"
    }) { id }
    }
    case class UpdateTaskParam(taskId: String,
    taskName: Option[String],
    taskDescription: Option[String],
    assignedTo: Option[String])
    54

    View Slide

  55. w ͍ͭ͜΋ϚΫϩͰࣗಈಋग़ग़དྷΔ
    *OQVU0CKFDU5ZQF
    case class UpdateTaskParam(taskId: String,
    taskName: Option[String],
    taskDescription: Option[String],
    assignedTo: Option[String])
    55
    implicit val paramJson = jsonFormat4(UpdateTaskParam.apply)
    val param = derive.deriveInputObjectType[UpdateTaskParam]()
    mutation {
    UpdateTask(attributes: {
    taskId: "t1",
    taskName: "newName"
    }) { id }
    }

    View Slide

  56. w &YFDVUPSʹεΩʔϚɺ࣮ߦ͍ͨ͠ΫΤϦɺ$POUFYU
    ͳͲΛ༩͑Δͱ+40/Λฦͯ͘͠ΕΔ
    (SBQI2-ͷ࣮ߦ
    val result: Future[JsValue] =
    Executor.execute[Ctx, Unit, JsObject](
    schema,
    document,
    context
    )
    56
    w ఆٛͨ͠(SBQI2-εΩʔϚ
    w ϦΫΤετͷ+40/Λύʔεͨ݁͠Ռ
    w ࣮ߦ͢ΔͨΊͷDPOUFYU

    View Slide

  57. 4DBMBͰͷ(SBQI2-
    w 4DBMBͱ૬ੑόπάϯ
    w ڧྗͳܕγεςϜͰ(SBQI2-ͷܕΛදݱ
    w ϚΫϩͰϘΠϥʔϓϨʔτ΋࡟ݮ
    w σϑΥϧτOPOOVMM࠷ߴ
    w 0QUJPOΛ͚ͭͳ͍ͱOVMMBCMFʹͳΒͳ͍
    w +40/ͱͷ૬ޓม׵
    w DBTFDMBTT͕͋ΔͷͰࣗಈಋग़Ͱ͖Δ
    w TBOHSJBͷ͓͔͛Ͱे෼ઓ͑Δ
    57

    View Slide

  58. (SBQI2-ͷ࣮ફ
    58

    View Slide

  59. (SBQI2-Ͱͷ࣮ફฤ
    w (SBQI2-Λ4DBMB4BOHSJBͰͲ͏࣮૷͍ͯ͠Δ͔
    w ͋͘·Ͱ͜͏΍࣮ͬͯફ͍ͯ͠Δɺͱ͍͏࿩
    w ͍Ζ͍Ζ೰ΈϙΠϯτ΋ଟ͍
    w (SBQI2-ͳΒͰ͸ͷ೰Έ΋͋Δ
    w খ͍͞΋ͷ͔Βେ͖͍΋ͷ·Ͱ͍Ζ͍Ζ
    59

    View Slide

  60. (SBQI2-Ͱͷ࣮ફฤ
    w ΞʔΩςΫνϟ
    w εΩʔϚͷઃܭ
    w $POUFYUΛͲ͏͢Δ͔
    w ೝূɺೝՄΛͲ͏͢Δ͔
    w τϥϯβΫγϣϯ
    w ΤϥʔϋϯυϦϯά
    w /໰୊
    w FUD
    60

    View Slide

  61. ΞʔΩςΫνϟ
    61

    View Slide

  62. ΞʔΩςΫνϟ
    w ϓϩάϥϛϯάϨϕϧͰͷઃܭ
    w (SBQI2-ʹͲ͜·Ͱ੹຿Λ೚ͤΔ͔
    w ͲΜͳײ͡Ͱ࡞Δͷ͕͍͍ͷ͔
    62

    View Slide

  63. ΞʔΩςΫνϟ
    HTTP
    GraphQL
    Business Logic
    Database Access
    63

    View Slide

  64. w (SBQI2-͸)551ϨΠϠͱϏδωεϩδοΫͷؒ
    ΛऔΓ࣋ͭଘࡏͱͯ͠ઃܭ͢Δͷ͕ྑ͍
    w (SBQI2-͸͋͘·Ͱ*'ͱͯ͠ར༻͢Δ
    ΞʔΩςΫνϟ
    HTTP
    GraphQL
    Business Logic
    Database Access
    64

    View Slide

  65. w (SBQI2-࡞ऀͷ-FF#ZSPOࢯ΋ݴ͍ͬͯΔ
    w ೝূೝՄɺΩϟογϡ΍%#ΫΤϦͳͲ͸
    (SBQI2-ϨΠϠͰ͸ͳͦ͘ͷԼͰ΍Δ΂͖
    w ަ׵Մೳੑ্͕͕ͬͯকདྷ໾ʹཱͭ
    ΞʔΩςΫνϟ
    65
    https://www.youtube.com/watch?v=zVNrqo9XGOs

    View Slide

  66. w (SBQI2-ͷར఺͸2VFSZʹ͋ΒΘΕΔ
    w ΫϥΠΞϯτͷཁٻʹԠ͑Δॊೈͳ2VFSZ
    w .VUBUJPO͸ਖ਼௚ͦ͜·Ͱʜ
    ΞʔΩςΫνϟ
    66

    View Slide

  67. w $234
    w $PNNBOE2VFSZ3FTQPOTJCJMJUZ4FHSFHBUJPO
    ΞʔΩςΫνϟ
    67
    https://docs.microsoft.com/ja-jp/azure/architecture/guide/architecture-styles/cqrs

    View Slide

  68. ΞʔΩςΫνϟ
    HTTP
    GraphQL
    Business Logic
    Database Access
    68

    View Slide

  69. ΞʔΩςΫνϟ
    69
    HTTP
    GraphQL
    UseCase/Application
    Database Access(DAO)
    Query Mutation
    Domain
    3FQPTJUPSZ

    Resolver

    View Slide

  70. w $234Λ؆ུԽͯ͠࠾༻͍ͯ͠Δ
    w ߋ৽ܥ͸%%%Ͱ͖ͬͪΓϞσϦϯά࣮૷
    w ࢀরܥ͸TFMFDUͨ͠σʔλΛՃ޻͢Δ͚ͩ
    w (SBQI2-ͷ2VFSZ͸ͲΜͳσʔλΛඞཁͱ͞ΕΔ
    ͔ɺݺͼग़͞ΕΔ·ͰΘ͔Βͳ͍
    w %%%3FQPTJUPSZ͸ݫ͍͠
    w ఘΊ΋େࣄ
    w %BUB"DDFTT0CKFDUΛ࢖͏
    ΞʔΩςΫνϟ
    70

    View Slide

  71. w ͱ͸͍͑2VFSZܥͷSFTPMWFͰ%"0Λ௚઀ݺͿͱɺ
    %"0ЋͳॲཧΛͨ͘͠ͳͬͨ࣌ʹࠔΔ
    w ΩϟογϡೖΕͨΓϖʔδωʔγϣϯͨ͠Γ
    w 3FTPMWFS૚Λ༻ҙ͓ͯ͘͠ͱྑ͍
    w ಺෦Ͱ͸%"0Ћఔ౓
    w SFTPMWFؔ਺͔Β࢖ΘΕΔ
    ΞʔΩςΫνϟ
    71

    View Slide

  72. w 3FTPMWFSͷ࣮૷ྫ
    ΞʔΩςΫνϟ
    72
    Field("users", ListType(userType), resolve = { ctx =>
    UserResolver.all()(ctx.ctx)
    })
    object UserResolver {
    def all()(implicit ctx: GraphQLContext): Future[Seq[User]] = {
    withCache("user#all") {
    UserDao.findAll()
    }
    }
    }

    View Slide

  73. εΩʔϚ
    73

    View Slide

  74. εΩʔϚ
    w ΫϥΠΞϯτʹͲΜͳεΩʔϚΛఏڙ͢Δ͔
    w 3&45GVMͳ"1*ʹ͓͚Δ63-ઃܭʹ͍ۙ
    w ·͞ʹϦιʔεઃܭ
    w εΩʔϚͷେ͖ͳ෼͚ํ
    w ࢀরܥ͸2VFSZ
    w ߋ৽ܥ͸.VUBUJPO
    74

    View Slide

  75. εΩʔϚ 2VFSZ

    w SPPUϨϕϧ͸ඇৗʹॏཁ
    w ʮ(SBQI2-͸ΫϥΠΞϯτ͕ඞཁͳσʔλ͚ͩ
    Λબ୒ͯ͠औಘ͢Δ͜ͱ͕ग़དྷΔʯͷى఺
    w ͔͜͜Βؔ࿈Λ୧͍ͬͯ͘͜ͱʹͳΔ
    type Query {
    projects: [Project!]!
    tasks(status: TaskStatus): [Task!]!
    users(arg: UserSearchCondition): [User!]!
    }
    75

    View Slide

  76. εΩʔϚ 2VFSZ

    w AWJFXFSA
    w ϦΫΤετ͖ͯͨ͠Ϣʔβࣗ਎ʹඥͮ͘৘ใ
    w ࣗ෼ࣗ਎ΛOPOOVMMͰఏڙͨ͠Γ
    type Query {
    projects: [Project!]!
    tasks: [Task!]!
    users: [User!]!
    viewer: ViewerQuery!
    }
    type ViewerQuery {
    self: User!
    assignedTasks: [Task!]!
    belongsTo: [Project!]!
    }
    76

    View Slide

  77. εΩʔϚ 2VFSZ

    w AWJFXFSA
    w ϦΫΤετ͖ͯͨ͠Ϣʔβࣗ਎ʹඥͮ͘৘ใ
    w ࣗ෼ࣗ਎ΛOPOOVMMͰఏڙͨ͠Γ
    w (JUIVCWΛύΫΔ
    77

    View Slide

  78. εΩʔϚ .VUBUJPO

    w શͯSPPUϨϕϧʹฒ΂Δͷ͕Φεεϝ
    w ωετͨ͠.VUBUJPO͸࣮ߦॱং͕อূ͞Εͳ͍
    mutation {
    CreateTask(…) { id }
    UpdateTask(…) { id } // CreateTaskͷޙʹ࣮ߦ͞ΕΔ
    }
    mutation {
    Task {
    Create(…) { id }
    Update(…) { id } // Createͷޙʹ࣮ߦ͞ΕΔอূ͸ͳ͍
    }
    }
    78

    View Slide

  79. εΩʔϚ .VUBUJPO

    w "SHVNFOUͱ*OQVU0CKFDU5ZQF͸͓޷ΈͰྑͦ͞͏
    w 63-ύϥϝʔλPS+40/ͷΠϝʔδ
    w *OQVU0CKFDU5ZQF͸DBTFDMBTTʹύʔεग़དྷͯศར
    79
    *OQVU0CKFDU5ZQF
    "SHVNFOU

    View Slide

  80. όʔδϣχϯά
    80

    View Slide

  81. όʔδϣχϯά
    w Α͋͘Δޡղ
    w ʮ(SBQI2-ͩͱόʔδϣχϯά͠ͳͯ͘Α͍ʯ
    81

    View Slide

  82. όʔδϣχϯά
    w "1*ͷύεͱͯ͠όʔδϣχϯά͸ෆཁ
    w WHSBQIRM
    w WHSBQIRM
    82
    "

    View Slide

  83. όʔδϣχϯά
    w εΩʔϚͷόʔδϣχϯά͸ඞཁ
    w ϑΟʔϧυͷ௥ՃͷΈɺͩͱෆཁ
    w มߋ͸!EFQFSFDBUFE͚ͭͯɺมߋޙͷϑΟʔϧ
    υผ໊Ͱ௥Ճɺ͠͹Βͨ͘͠ΒมߋલͷΛফ͢
    w ࡟আ͸!EFQFSFDBUFE͚ͭͯ͠͹Βͨ͘͠Βফ͢
    w %#ͷεΩʔϚͱಉ͡Α͏ͳϑϩʔ
    w ϑΟʔϧυ໊͸ඇৗʹॏཁ
    w มߋͮ͠Β͍ɺͱ͍͏ͷΛ೦಄ʹஔ͍͓ͯ͘
    83

    View Slide

  84. όʔδϣχϯά
    w 'JFMEʹAEFQSFDBUFE3FBTPOAΛ༩͑Δ
    84
    Field("name", StringType,
    resolve = { ctx => ctx.value.name }),
    Field("displayName", StringType,
    deprecationReason = Some("use `name` instead"),
    resolve = { ctx => ctx.value.name }),

    View Slide

  85. (SBQI2-ʹ͓͚Δ
    $POUFYU
    85

    View Slide

  86. val userType: ObjectType[Ctx, User] = ObjectType[Ctx, User](
    "User", fields[Ctx, User](
    Field("id", IDType, resolve = { _.value.id }),
    Field("name", StringType, resolve = {
    ctx: Context[Ctx, User] => ctx.value.name }),
    Field("status", userStatusType, resolve = { _.value.status }),
    Field("projects", ListType(projectType), resolve = { ctx =>
    ProjectResolver.byIds(ctx.value.projectIds)(ctx.ctx)
    })
    )
    )
    0CKFDU5ZQF
    86

    View Slide

  87. (SBQI2-ʹ͓͚Δ$POUFYU
    w (SBQI2-ͷܕʹ͓͚ΔSFTPMWFΛఆٛ͢Δࡍʹొ৔
    ͢Δ$POUFYUʹԿΛ࣋ͨͤΔ͔
    w ʮ$POUFYUʯͱ͍͏໊લ͕೉͍͠
    w DPOUFYUVBMͳ৘ใͬͯԿʁ
    87

    View Slide

  88. (SBQI2-ʹ͓͚Δ$POUFYU
    w ྫϩάΠϯதͷϢʔβ΍%#΁ͷΞΫηεΛ࣋ͭ
    context"WBMVFXIJDIJTQSPWJEFEUPFWFSZSFTPMWFSBOEIPMET
    JNQPSUBOUDPOUFYUVBMJOGPSNBUJPOMJLFUIFDVSSFOUMZMPHHFEJOVTFS
    PSBDDFTTUPBEBUBCBTF
    IUUQTHSBQIRMPSHMFBSOFYFDVUJPOSPPUpFMETSFTPMWFST
    88

    View Slide

  89. (SBQI2-ʹ͓͚Δ$POUFYU
    w %#ΞΫηεՄೳͳ4FSWJDF3FQPTJUPSZͳͲ
    89
    IUUQTTBOHSJBHSBQIRMPSHMFBSOJOQVUBOEDPOUFYUPCKFDUT

    View Slide

  90. (SBQI2-ʹ͓͚Δ$POUFYU
    w ͳΔ΄Ͳɺ%"0΍4FSWJDF΍3FQPTJUPSZΛ$POUFYU
    ͔ΒΞΫηεग़དྷΔΑ͏ʹͨ͠Βྑ͍Μͩͳʂ
    90

    View Slide

  91. (SBQI2-ʹ͓͚Δ$POUFYU
    w ͳΔ΄Ͳɺ%"0΍4FSWJDF΍3FQPTJUPSZΛ$POUFYU
    ͔ΒΞΫηεग़དྷΔΑ͏ʹͨ͠Βྑ͍Μͩͳʂ
    91
    ͓͢͢Ί͠ͳ͍

    View Slide

  92. (SBQI2-ʹ͓͚Δ$POUFYU
    w %"0͕શͯͷϑΟʔϧυ͔Β$POUFYUܦ༝Ͱ޷͖
    উखʹΞΫηεग़དྷΔΑ͏ʹͳͬͯ͠·ͬͯϨΠ
    Ϡʔ͕յΕΔ
    w SFTPMWFؔ਺ͰશͯΛ΍ͬͯ͠·͑Δঢ়ଶʹ
    w ݁Ռͱͯ͠(SBQI2-ϨΠϠ͕ް͘ͳΔ
    92

    View Slide

  93. (SBQI2-ʹ͓͚Δ$POUFYU
    w $POUFYU͸ಉҰϦΫΤετ಺Ͱڞ௨ͯ͠ࢀরՄೳ
    w (SBQI2-ΫΤϦͷ࣮ߦ୯Ґ ϦΫΤετ
    Ͱڞ௨͠
    ͯࢀর͍ͨ͠΋ͷΛ࣋ͨͤΔ΂͖
    w ϩάΠϯϢʔβ
    w τϥϯβΫγϣϯ
    w &YFDVUJPO$POUFYU
    w FUD
    93

    View Slide

  94. w $POUFYUͷ࣮૷ྫ
    w ϩάΠϯϢʔβ
    w τϥϯβΫγϣϯ %#4FTTJPO

    w &YFDVUJPO$POUFYU
    class GraphQLContext(
    userOpt: Option[User],
    session: DBSession,
    ec: ExecutionContext
    )
    (SBQI2-ʹ͓͚Δ$POUFYU
    94

    View Slide

  95. ೝূೝՄ
    95

    View Slide

  96. ೝূೝՄ
    w 8FCΞϓϦέʔγϣϯͳΒେମ͋Δ΍ͭ
    w ೝূ "VUIFOUJDBUJPO

    w ୭͔Λಛఆ͢Δ
    w ೝՄ "VUIPSJ[BUJPO

    w ݖݶΛ֬ೝ͢Δ
    w (SBQI2-ͱͲ͏૊Έ߹ΘͤΔ΂͖͔ɺͱ͍͏࿩
    96

    View Slide

  97. ೝূ
    w ೝূͨ݁͠Ռ͸શମͰ࢖͍ճ͍ͨ͠
    w ෳ਺ͷ2VFSZ.VUBUJPOΛಉ࣌ʹ࣮ߦ͢Δࡍʹɺ
    ผʑʹೝূΛ͢Δͷ͸ྲྀੴʹڐ༰Ͱ͖ͳ͍
    w ೝূॲཧͦͷ΋ͷ͸(SBQI2-ͱ͸ؔ܎ͳ͍
    w $PPLJF࢖͓͏͕+85࢖͓͏͕͓޷͖ʹ
    97

    View Slide

  98. ೝূ
    w (SBQI2-ͷ$POUFYUΛ࡞੒͢ΔࡍʹೝূΛߦ͏
    w ೝূ݁ՌΛ$POUFYUʹೖΕͯ͠·͑͹ڞ༗ग़དྷΔ
    98
    object GraphQLContext {
    def create(userIdOpt: Option[String])(
    implicit ec: ExecutionContext, s: DBSession): GraphQLContext = {
    userIdOpt.flatMap { userId =>
    authenticate(userId).map { user =>
    new GraphQLContext(Some(user), ec, s)
    }
    } getOrElse { new GraphQLContext(None, ec, s) }
    }
    private def authenticate(userId: String): Option[User] = ???
    }
    GBDUPSZͰೝূͭͭ͠ੜ੒

    View Slide

  99. ೝূ
    w ೝূΛඞཁͱ͢Δ2VFSZ.VUBUJPOΛ࣮૷͢Δ
    w ೝূॲཧͦͷ΋ͷ͸(SBQI2-ͱ͸੾Γ཭͢
    w .JEEMFXBSF͕࢖͏ͱڧ੍Ͱ͖ͯศར
    w ΫΤϦ࣮ߦͷCFGPSFBGUFSʹॲཧΛࠩ͠ࠐΊΔ
    w TBOHSJBFYFDVUJPO഑ԼͷΫϥεΛར༻͢Δ
    w 'JFME5BH
    w .JEEMFXBSF.JEEMFXBSF#FGPSF'JFME
    99

    View Slide

  100. w ೝূඞਢʹ͍ͨ͠pFMEͷఆٛʹUBHΛ෇༩͢Δ
    w &YFDVUPSFYFDVUFʹNJEEMFXBSFͱͯ͠
    "VUIFOUJDBUJPO'JMUFSΛ༩͑Ε͹ಈ͘
    ೝূ
    val self: Field[Ctx, User] = Field(
    "self",
    userType,
    tags = Middlewares.RequireAuthentication :: Nil,
    resolve = { ctx => ctx.ctx.loggedInUser }
    )
    ೝূඞਢͳ͜ͱΛλάͰ໌ࣔ
    Executor.execute[Ctx, Unit, JsObject](
    schema, queryDocument, context,
    middleware = AuthenticationFilter :: Nil
    )
    100

    View Slide

  101. ೝূ
    type Ctx = GraphQLContext
    case object RequireAuthentication extends FieldTag
    object AuthenticationFilter extends MiddlewareBeforeField[Ctx] {
    override type QueryVal = Unit
    override type FieldVal = Unit
    private type MCtx = MiddlewareQueryContext[Ctx, _, _]
    override def beforeQuery(context: MCtx) = ()
    override def afterQuery(queryVal: QueryVal, context: MCtx) = ()
    override def beforeField(queryVal: QueryVal,
    mctx: MCtx,
    ctx: Context[Ctx, _]) = {
    if (ctx.field.tags contains RequireAuthentication) {
    if (!ctx.ctx.isLoggedIn) {
    throw AuthenticationError("must logged in!")
    }
    }
    continue
    }
    }
    101

    View Slide

  102. type Ctx = GraphQLContext
    case object RequireAuthentication extends FieldTag
    object AuthenticationFilter extends MiddlewareBeforeField[Ctx] {
    override type QueryVal = Unit
    override type FieldVal = Unit
    private type MCtx = MiddlewareQueryContext[Ctx, _, _]
    override def beforeQuery(context: MCtx) = ()
    override def afterQuery(queryVal: QueryVal, context: MCtx) = ()
    override def beforeField(queryVal: QueryVal,
    mctx: MCtx,
    ctx: Context[Ctx, _]) = {
    if (ctx.field.tags contains RequireAuthentication) {
    if (!ctx.ctx.isLoggedIn) {
    throw AuthenticationError("must logged in!")
    }
    }
    continue
    }
    }
    ೝূ
    ೝূඞਢͳϑΟʔϧυʹ෇༩͢Δ5BH
    102

    View Slide

  103. type Ctx = GraphQLContext
    case object RequireAuthentication extends FieldTag
    object AuthenticationFilter extends MiddlewareBeforeField[Ctx] {
    override type QueryVal = Unit
    override type FieldVal = Unit
    private type MCtx = MiddlewareQueryContext[Ctx, _, _]
    override def beforeQuery(context: MCtx) = ()
    override def afterQuery(queryVal: QueryVal, context: MCtx) = ()
    override def beforeField(queryVal: QueryVal,
    mctx: MCtx,
    ctx: Context[Ctx, _]) = {
    if (ctx.field.tags contains RequireAuthentication) {
    if (!ctx.ctx.isLoggedIn) {
    throw AuthenticationError("must logged in!")
    }
    }
    continue
    }
    }
    ೝূ
    0CKFDU5ZQF౳ͷpFMEʹࠩ͠ࠐΉॲཧ
    103

    View Slide

  104. type Ctx = GraphQLContext
    case object RequireAuthentication extends FieldTag
    object AuthenticationFilter extends MiddlewareBeforeField[Ctx] {
    override type QueryVal = Unit
    override type FieldVal = Unit
    private type MCtx = MiddlewareQueryContext[Ctx, _, _]
    override def beforeQuery(context: MCtx) = ()
    override def afterQuery(queryVal: QueryVal, context: MCtx) = ()
    override def beforeField(queryVal: QueryVal,
    mctx: MCtx,
    ctx: Context[Ctx, _]) = {
    if (ctx.field.tags contains RequireAuthentication) {
    if (!ctx.ctx.isLoggedIn) {
    throw AuthenticationError("must logged in!")
    }
    }
    continue
    }
    }
    ೝূ
    3FRVJSF"VUIFOUJDBUJPOλά͕෇͍ͯΔͷʹ
    ϩάΠϯͯ͠ͳ͍৔߹͸ྫ֎Λૹग़
    104

    View Slide

  105. ೝՄ
    w ೝՄ͸ΞϓϦέʔγϣϯ૚Ͱߦ͏
    w (SBQI2-ʹ͸ؔ܎ͳ͍࿩ͳͷͰׂѪ
    105

    View Slide

  106. %#τϥϯβΫγϣϯ
    106

    View Slide

  107. %#τϥϯβΫγϣϯ
    w Ͳͷ୯ҐͰτϥϯβΫγϣϯΛ࣮ߦ͢Δ͔
    w ͍ͭ#FHJO͍ͯͭ͠$PNNJU3PMMCBDL͢Δ͔
    w (SBQI2-ΫΤϦ͸ෳ਺ͷ2VFSZ.VUBUJPOΛ·ͱ
    Ί࣮ͯߦ͢Δ͜ͱ͕ग़དྷΔ
    w ಛʹ.VUBUJPO͸ߋ৽ܥͳͷͰτϥϯβΫγϣϯ
    Λผʑʹ͍ͨ͠ͱ͍͏ཉٻ͸͋Δ͸ͣ
    107

    View Slide

  108. %#τϥϯβΫγϣϯ
    w ݁࿦ɺϦΫΤετʹରͯ͠τϥϯβΫγϣϯ
    w ຊ౰ʹτϥϯβΫγϣϯΛผʹ͍ͨ͠ૢ࡞ΛϦ
    ΫΤετʹ৐ͤͯ·ͱΊ࣮ͯߦ͢Δ͜ͱ͕͋Δ
    ͩΖ͏͔ʁ
    108

    View Slide

  109. %#τϥϯβΫγϣϯ
    w τϥϯβΫγϣϯͷ੹຿͕(SBQI2-ϨΠϠʹʜ
    w $POUFYUʹ࣋ͨͤͯΫΤϦؒͰڞ༗͢Δ
    w ઃܭతʹ͸ͪΐͬͱඍົͳؾ࣋ͪ
    w ԿΛ༏ઌ͢Δ͔Ͱܾఆ͢Δ΂͖
    109

    View Slide

  110. ΤϥʔϋϯυϦϯά
    110

    View Slide

  111. ΤϥʔϋϯυϦϯά
    w (SBQI2-Ͱ͸εςʔλείʔυ͸جຊతʹ
    w Τϥʔ͸ϨεϙϯεϘσΟʹೖΕͯฦ٫͞ΕΔ
    111

    View Slide

  112. ΤϥʔϋϯυϦϯά
    w ෳ਺ͷ2VFSZ.VUBUJPOΛ࣮ߦͨ͠ࡍʹɺͲΕ͔
    ͭͰ΋ࣦഊͨ͠Βશମ͕Τϥʔʹͳͬͯ͠·͏
    ͱ͍͏໰୊͕͋Δ
    w ྫɿτοϓϖʔδͷදࣔʹඞཁͳϦιʔεΛ·
    ͱΊͯऔಘͨ͠ΒɺҰ෦෼͚ͩऔಘʹࣦഊͨ͠
    ͚ͩͰશମ͕ਅͬനʹͳΔ
    w ϚΠΫϩαʔϏε࣌୅ʹ͙ͦΘͳ͍ײ
    w #''ʹ࢖͍ਏ͍ʜ
    112

    View Slide

  113. ΤϥʔϋϯυϦϯά
    w ͍Ζ͍Ζબ୒ࢶ͸͋Δ
    w શ෦औಘͰ͖ͳ͍ͱҙຯແ͍୯ҐͰΫΤϦൃߦ
    w গʑࣦഊͯ͠΋ϦτϥΠ͢Ε͹͑͑΍Μ
    w FUD
    w ੒ޭࣦഊΛಉډͤ͞Δͷ͸͔ͳΓେมͦ͏
    w 6OJPO5ZQFʹͯ͠੒ޭࣦഊΛදݱ͢Δͱ͔ʁ
    w SFTPMWFͰྫ֎͕౤͛ΒΕͳ͍Α͏ʹέΞ͕ඞཁ
    113

    View Slide

  114. ΤϥʔϋϯυϦϯά
    w ͰɺͲ͏͍ͯ͠Δ͔
    w Ұ෦ͷΤϥʔΛશମͷΤϥʔͱ͍ͯ͠Δ
    w ࣮૷ίετͱͷ݉Ͷ߹͍
    w Ұ෦Ͱ΋ࣦഊ͢ΔͷͬͯԿ͔͕͓͔͍͠
    w #''ͳΒಛఆͷΞϓϦ͕ෆ௨ʹͳΔՄೳੑ΋
    w ୯ҰΞϓϦͳΒ૝ఆ͢Δඞཁͳ͍
    114

    View Slide

  115. ΤϥʔϋϯυϦϯά
    w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ
    w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ
    sealed abstract class ErrorType(val value: String)
    object ErrorType {
    case object BadRequest extends ErrorType("BadRequest")
    case object ServerError extends ErrorType("ServerError")
    case object AuthError extends ErrorType("AuthError")
    }
    implicit val error = derive.deriveEnumType[ErrorType]()
    115

    View Slide

  116. ΤϥʔϋϯυϦϯά
    w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ
    w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ
    sealed abstract class ErrorType(val value: String)
    object ErrorType {
    case object BadRequest extends ErrorType("BadRequest")
    case object ServerError extends ErrorType("ServerError")
    case object AuthError extends ErrorType("AuthError")
    }
    implicit val error = derive.deriveEnumType[ErrorType]()
    def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) =
    HandledException(
    msg,
    additionalFields = Map("type" -> m.enumNode(tp.value, error.name)),
    addFieldsInExtensions = false,
    addFieldsInError = true
    )
    )BOEMFE&YDFQUJPOΛ࢖͑͹
    (SBQI2-ͬΆ͍ΤϥʔϝοηʔδΛฦ٫Ͱ͖Δ
    116

    View Slide

  117. ΤϥʔϋϯυϦϯά
    w Τϥʔͷछྨ͚ͩΘ͔ΔΑ͏ʹ
    w ϦΫΤετෆਖ਼αʔόΤϥʔೝূΤϥʔ
    sealed abstract class ErrorType(val value: String)
    object ErrorType {
    case object BadRequest extends ErrorType("BadRequest")
    case object ServerError extends ErrorType("ServerError")
    case object AuthError extends ErrorType("AuthError")
    }
    implicit val error = derive.deriveEnumType[ErrorType]()
    def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) =
    HandledException(
    msg,
    additionalFields = Map("type" -> m.enumNode(tp.value, error.name)),
    addFieldsInExtensions = false,
    addFieldsInError = true
    )
    117

    View Slide

  118. ΤϥʔϋϯυϦϯά
    w ϋϯυϦϯά͢Δ΂͖Τϥʔ͕ଟ͍
    w +40/ͷTZOUBYΤϥʔ
    w ΫΤϦͷTZOUBYΤϥʔɺWBMJEBUJPOΤϥʔ
    w ϏδωεϩδοΫ্ͷΤϥʔ
    w ͦͷଞɺUISPX͞ΕͨΤϥʔ
    w Ͳ͏΍ͬͯϋϯυϦϯά͢Δʁ
    w &YDFQUJPO)BOEMFSΛؤு࣮ͬͯ૷͢Δ
    w &YFDVUPSFYFDVUFͷSFDPWFSͰ΋ؤுΔ
    118

    View Slide

  119. ΤϥʔϋϯυϦϯά
    private val exceptionHandler = {
    def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) =
    HandledException(
    msg,
    additionalFields = Map("type" -> m.enumNode(tp.value, errorType.name)),
    addFieldsInExtensions = false,
    addFieldsInError = true
    )
    val onException: PartialFunction[(ResultMarshaller, Throwable), HandledException] = {
    case (m, qa: QueryAnalysisError) =>
    logger.warn(s"QueryAnalysisError occurred. msg = ${qa.getMessage}")
    handleException(m, qa.getMessage, ErrorType.BadRequest)
    case (m, se: SyntaxError) =>
    logger.info(s"SyntaxError occurred. msg = ${se.getMessage()}")
    handleException(m, se.getMessage, ErrorType.BadRequest)
    case (m, ewr: ErrorWithResolver) =>
    logger.error(s"ErrorWithResolver occurred. msg = ${ewr.getMessage}", ewr)
    handleException(m, ewr.getMessage, ErrorType.ServerError)
    case (m, AuthenticationError(msg)) =>
    logger.info(s"AuthenticationError occurred. msg = ${msg}")
    handleException(m, msg, ErrorType.BadRequest)
    case (m, NotFoundException(msg)) =>
    logger.info(s"NotFoundException occurred. msg = ${msg}")
    handleException(m, msg, ErrorType.BadRequest)
    case (m, t: Throwable) =>
    logger.error(s"unknown server error occurred. msg = ${t.getMessage}", t)
    handleException(m, t.getMessage, ErrorType.ServerError)
    }
    val onViolation: PartialFunction[(ResultMarshaller, Violation), HandledException] = {
    case (m, v) =>
    logger.warn(s"Violation error occurred. msg = ${v.errorMessage}")
    handleException(m, v.errorMessage, ErrorType.BadRequest)
    }
    val onUserFacingError
    : PartialFunction[(ResultMarshaller, UserFacingError), HandledException] = {
    case (m, v: WithViolations) =>
    logger.warn(s"WithViolations occurred. msg = ${v.getMessage()}")
    handleException(m, v.getMessage, ErrorType.BadRequest)
    case (m, ie: InternalError) =>
    logger.error(s"InternalError occurred. msg = ${ie.getMessage()}", ie)
    handleException(m, ie.getMessage, ErrorType.ServerError)
    case (m, ee: ExecutionError) =>
    logger.error(s"ExecutionError occurred. msg = ${ee.getMessage()}", ee)
    handleException(m, ee.getMessage, ErrorType.ServerError)
    case (m, ufe) =>
    logger.warn(s"UserFacingError occurred. msg = ${ufe.getMessage()}")
    handleException(m, ufe.getMessage, ErrorType.BadRequest)
    }
    ExceptionHandler(onException, onViolation, onUserFacingError)
    }
    Future.fromTry(QueryParser.parse(document)) zip GraphQLContext.create(userIdOpt) flatMap {
    case (queryDocument: Document, context: GraphQLContext) =>
    val result: Future[JsValue] =
    Executor.execute[Ctx, Unit, JsObject](
    schema,
    queryDocument,
    context,
    exceptionHandler = exceptionHandler,
    operationName = operation,
    variables = vars,
    middleware = Middlewares.value
    )
    result
    .map { jsValue =>
    OK -> jsValue
    }
    .recover {
    case error: QueryAnalysisError =>
    BadRequest -> error.resolveError
    case error: ErrorWithResolver =>
    InternalServerError -> error.resolveError
    }
    } recover {
    case v: SyntaxError =>
    BadRequest -> new Exception(v.getMessage()) with ErrorWithResolver with WithViolations {
    override def exceptionHandler: ExceptionHandler = GraphQLServer.exceptionHandler
    override def violations: Vector[Violation] = Vector.empty
    }.resolveError
    case v: ValidationError =>
    BadRequest -> v.resolveError
    case NonFatal(t) =>
    InternalServerError -> InternalError(t, GraphQLServer.exceptionHandler).resolveError
    }
    119

    View Slide

  120. ΤϥʔϋϯυϦϯά
    private val exceptionHandler = {
    def handleException(m: ResultMarshaller, msg: String, tp: ErrorType) =
    HandledException(
    msg,
    additionalFields = Map("type" -> m.enumNode(tp.value, errorType.name)),
    addFieldsInExtensions = false,
    addFieldsInError = true
    )
    val onException: PartialFunction[(ResultMarshaller, Throwable), HandledException] = {
    case (m, qa: QueryAnalysisError) =>
    logger.warn(s"QueryAnalysisError occurred. msg = ${qa.getMessage}")
    handleException(m, qa.getMessage, ErrorType.BadRequest)
    case (m, se: SyntaxError) =>
    logger.info(s"SyntaxError occurred. msg = ${se.getMessage()}")
    handleException(m, se.getMessage, ErrorType.BadRequest)
    case (m, ewr: ErrorWithResolver) =>
    logger.error(s"ErrorWithResolver occurred. msg = ${ewr.getMessage}", ewr)
    handleException(m, ewr.getMessage, ErrorType.ServerError)
    case (m, AuthenticationError(msg)) =>
    logger.info(s"AuthenticationError occurred. msg = ${msg}")
    handleException(m, msg, ErrorType.BadRequest)
    case (m, NotFoundException(msg)) =>
    logger.info(s"NotFoundException occurred. msg = ${msg}")
    handleException(m, msg, ErrorType.BadRequest)
    case (m, t: Throwable) =>
    logger.error(s"unknown server error occurred. msg = ${t.getMessage}", t)
    handleException(m, t.getMessage, ErrorType.ServerError)
    }
    val onViolation: PartialFunction[(ResultMarshaller, Violation), HandledException] = {
    case (m, v) =>
    logger.warn(s"Violation error occurred. msg = ${v.errorMessage}")
    handleException(m, v.errorMessage, ErrorType.BadRequest)
    }
    val onUserFacingError
    : PartialFunction[(ResultMarshaller, UserFacingError), HandledException] = {
    case (m, v: WithViolations) =>
    logger.warn(s"WithViolations occurred. msg = ${v.getMessage()}")
    handleException(m, v.getMessage, ErrorType.BadRequest)
    case (m, ie: InternalError) =>
    logger.error(s"InternalError occurred. msg = ${ie.getMessage()}", ie)
    handleException(m, ie.getMessage, ErrorType.ServerError)
    case (m, ee: ExecutionError) =>
    logger.error(s"ExecutionError occurred. msg = ${ee.getMessage()}", ee)
    handleException(m, ee.getMessage, ErrorType.ServerError)
    case (m, ufe) =>
    logger.warn(s"UserFacingError occurred. msg = ${ufe.getMessage()}")
    handleException(m, ufe.getMessage, ErrorType.BadRequest)
    }
    ExceptionHandler(onException, onViolation, onUserFacingError)
    }
    Future.fromTry(QueryParser.parse(document)) zip GraphQLContext.create(userIdOpt) flatMap {
    case (queryDocument: Document, context: GraphQLContext) =>
    val result: Future[JsValue] =
    Executor.execute[Ctx, Unit, JsObject](
    schema,
    queryDocument,
    context,
    exceptionHandler = exceptionHandler,
    operationName = operation,
    variables = vars,
    middleware = Middlewares.value
    )
    result
    .map { jsValue =>
    OK -> jsValue
    }
    .recover {
    case error: QueryAnalysisError =>
    BadRequest -> error.resolveError
    case error: ErrorWithResolver =>
    InternalServerError -> error.resolveError
    }
    } recover {
    case v: SyntaxError =>
    BadRequest -> new Exception(v.getMessage()) with ErrorWithResolver with WithViolations {
    override def exceptionHandler: ExceptionHandler = GraphQLServer.exceptionHandler
    override def violations: Vector[Violation] = Vector.empty
    }.resolveError
    case v: ValidationError =>
    BadRequest -> v.resolveError
    case NonFatal(t) =>
    InternalServerError -> InternalError(t, GraphQLServer.exceptionHandler).resolveError
    }
    ΍Δ͔͠ͳ͍
    120

    View Slide

  121. /໰୊
    121

    View Slide

  122. /໰୊
    w (SBQI2-Ͱ͸ϦΫΤετΛճʹݻఆͰ͖Δ
    w ؔ࿈Λ୧ͬͯඞཁͳσʔλΛऔಘ͢Δ
    w ݁Ռͱͯ͠/ͳ42-͕ൃߦ͞Ε͕ͪ
    122

    View Slide

  123. /໰୊
    QSPKFDU͕/ݸ͋Ε͹
    VTFST΋/ճऔಘ͢Δɺ
    ·͞ʹ/ͳঢ়ଶ
    123

    View Slide

  124. /໰୊
    w /Λճආ͢ΔͨΊͷ࢓૊Έ͕ఏڙ͞Ε͍ͯΔ
    w %FGFSSFE3FTPMWFS
    w 'FUDIFS
    w SFTPMWF͢ΔͷΛ஗Βͤͯɺ·ͱΊͯऔಘͰ͖Δ΋
    ͷΛ·ͱΊͯऔಘ͢ΔΑ͏ʹ͢Δ
    w pOE#Z*E/ΛpOE#Z*ETʹ͢Δ͚ͩͳΠϝʔδ
    w Ωϟογϡͨ͠Γ΋Մೳ
    124

    View Slide

  125. object UserResolver {
    private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id)
    val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = {
    Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) =>
    UserDao.findByIds(ids)(ctx.ec)
    }
    }
    def byId(userId: UserId)(
    implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = {
    DeferredValue(userFetcher.defer(userId))
    }
    }
    /໰୊
    125

    View Slide

  126. object UserResolver {
    private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id)
    val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = {
    Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) =>
    UserDao.findByIds(ids)(ctx.ec)
    }
    }
    def byId(userId: UserId)(
    implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = {
    DeferredValue(userFetcher.defer(userId))
    }
    }
    /໰୊
    126
    4FR<6TFS*E>4FR<6TFS>ͳॲཧ

    View Slide

  127. object UserResolver {
    private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id)
    val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = {
    Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) =>
    UserDao.findByIds(ids)(ctx.ec)
    }
    }
    def byId(userId: UserId)(
    implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = {
    DeferredValue(userFetcher.defer(userId))
    }
    }
    /໰୊
    127
    4FR<6TFS*E>4FR<6TFS>ͳॲཧΛ
    ϥοϓ͢Δ'FUDIFS

    View Slide

  128. object UserResolver {
    private implicit lazy val hasId: HasId[User, UserId] = HasId(_.id)
    val userFetcher: Fetcher[GraphQLContext, User, User, UserId] = {
    Fetcher.apply { (ctx: GraphQLContext, ids: Seq[UserId]) =>
    UserDao.findByIds(ids)(ctx.ec)
    }
    }
    def byId(userId: UserId)(
    implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, User] = {
    DeferredValue(userFetcher.defer(userId))
    }
    }
    /໰୊
    128
    'FUDIFSEFGFSͰ*%Λ౉͢ͱ
    ͍͍ײ͡ʹ/Λ௵ͯ͘͠ΕΔ

    View Slide

  129. /໰୊
    129
    w ؔ࿈Λ୧Δ/Λղফ͢Δͷ΋༻ҙ͞Ε͍ͯΔ
    object UserResolver {
    private val usersByProjects: Relation[User, User, ProjectId] =
    Relation[User, ProjectId]("byProjects", { u: User =>
    u.projectIds
    })
    lazy val userFetcherByProject: Fetcher[GraphQLContext, User, User, UserId] = {
    Fetcher.relOnly { (ctx: GraphQLContext, rel: RelationIds[User]) =>
    val ids: Seq[ProjectId] = rel.apply(usersByProjects)
    UserDao.findByProjectIds(ids)(ctx.ec)
    }
    }
    def findByProjectId(projectId: ProjectId)(
    implicit ctx: GraphQLContext): DeferredValue[GraphQLContext, Seq[User]] = {
    DeferredValue(userFetcherByProject.deferRelSeq(usersByProjects, projectId))
    }
    }

    View Slide

  130. ·ͱΊ
    130

    View Slide

  131. ·ͱΊ
    w (SBQI2-ศརͩ͠Φεεϝ
    w ۜͷ஄ؙͰ͸ͳ͍ ೋ౓໨

    w ࣍ͷϓϩδΣΫτͰ΋࠾༻͍ͨ͠
    w 3&45΍ͬͯͨࠒͱ೰ΉϙΠϯτ͕มΘΔ
    w Ͳ͕ͬͪྑ͍ɺͱ͍͏࿩Ͱ͸ͳ͍
    w 4DBMBͱ΋૬ੑ͸ྑ͍
    w 4BOHSJBΛ࢖͑͹े෼ઓ͑Δ
    131

    View Slide

  132. ࣮ફͰ͖ͦ͏Ͱ͠ΐ͏͔ʁ
    132

    View Slide

  133. ࠓ೔ͷΰʔϧ
    ʮ࣍ͷϓϩδΣΫτͰ͸
    ɹ(SBQI2-Λ 4DBMBͰ
    ΍Γ͍ͨʂʯ
    133

    View Slide

  134. ࣮ફ(SBQI2-PO4DBMB
    4DBMBؔ੢4VNNJU
    !QFUJUWJPMFU


    View Slide