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

プロトコル、インターフェースとしてのGraphQL

sonata
April 25, 2023

 プロトコル、インターフェースとしてのGraphQL

宣言的UIの状態管理とアーキテクチャSwiftUIとGraphQLによる実践
https://speakerdeck.com/sonatard/swiftui-graphql

GraphQLの誤解
https://speakerdeck.com/sonatard/rethinking-graphql

GraphQL実践ノウハウ
https://speakerdeck.com/sonatard/graphql-knowhow

sonata

April 25, 2023
Tweet

More Decks by sonata

Other Decks in Programming

Transcript

  1. 1
    ϓϩτίϧɺΠϯλʔϑΣʔεͱͯ͠ͷ
    (SBQI2-
    &ODSBGUαʔόʔͱΫϥΠΞϯτΛ݁Ϳٕज़
    !TPOBUBSEͦͳଠ

    View Slide

  2. Stack CTO
    ͦͳଠ
    @sonatard

    View Slide

  3. 3
    ఏڙαʔϏε
    w "QQJGZ
    w 4IPQJGZͷΞϓϦΛ/P$PEFͰϦϦʔε
    w J04ɺ"OESPJE
    w 8FC্ͰΞϓϦͷσβΠϯΛมߋՄೳ
    w 7*1
    w 4IPQJGZʹॊೈͳձһϓϩάϥϜΛఏڙ
    w ϙΠϯτɺձһϥϯΫɺϦϫʔυɺ༑ୡ঺հ
    w ৽αʔϏε
    w ։ൃத

    View Slide

  4. 4
    ٕज़ελοΫ
    w J04
    w 4XJGU6*
    w "QPMMPJ04
    w "OESPJE
    w +FUQBDL$PNQPTF
    w "QPMMP,PUMJO
    w 8FC
    w 5ZQF4DSJQU
    w "QPMMP
    w #BDLFOE
    w (P
    w HRMHFO
    w ($1$MPVE3VOͳͲ

    View Slide

  5. 5
    4UBDLࣾͷ"1*ͷྺ࢙
    w ೥
    w 1SPUPDPM#V
    ff
    FSTPO)551
    w ೥Ҏ߱
    w (SBQI2-

    View Slide

  6. (SBQI2-ͱ͸

    View Slide

  7. 7
    (SBQI2-ͱ͸
    w جຊ
    w 0WFSGFUDIͷղফ
    w ඞཁͳ஋͚ͩΛऔಘ
    w 6OEFSGFUDIͷղফ
    w ౓ͷϦΫΤετͰඞཁͳ஋Λ͢΂ͯऔಘ
    w εΩʔϚۦಈ։ൃ
    w εΩʔϚ͔ΒόοΫΤϯυͱΫϥΠΞϯτͷίʔυΛੜ੒
    w ୯ҰΤϯυϙΠϯτʹ2VFSZΛૹ৴͢Δ
    w IUUQTFYNBQMFDPNHSBQIRM

    View Slide

  8. 8
    2VFSZ஋ͷऔಘ
    type Query {
    viewer: User!
    }
    type User implements Node {
    id: ID!
    order: Order!
    }
    type Order implements Node {
    id: ID!
    # default஋ΛઃఆͰ͖Δ


    product(hasStock: Boolean = true): Product!
    }
    type Product implements Node {
    id: ID!
    name: String!
    }
    query GetProduct {
    viewer {
    order {
    product {
    name
    }
    }
    }
    }
    4DIFNB 2VFSZ

    View Slide

  9. 9
    .VUBUJPO࡞੒ɺߋ৽ɺ࡟আͳͲ
    type Mutation {
    productCreate(input: ProductInput!):
    ProductCreatePayload!
    }
    input ProductInput {
    name: String!
    price: String!
    }
    type ProductCreatePayload {
    product: Product
    }
    mutation ProductCreate($input: ProductInput!){
    productCreate(input: $input) {
    product {
    id
    Name
    }
    }
    }
    4DIFNB 2VFSZ

    View Slide

  10. 10
    4VCTDSJQUJPOετϦʔϛϯάͰ஋Λऔಘ
    type Subscription {
    viewer: User!
    }
    type User implements Node {
    id: ID!
    order: Order!
    }
    type Order implements Node {
    id: ID!
    product: Product!
    }
    type Product implements Node {
    id: ID!
    name: String!
    }
    subscription GetProduct {
    viewer {
    order {
    product {
    name
    }
    }
    }
    }
    4DIFNB 2VFSZ

    View Slide

  11. 11
    εΩʔϚۦಈ։ൃ
    w (SBQI2-4DIFNBΛݩʹؔ࿈͢Δίʔυ͕ੜ੒͢ΔͷͰɺܕͷෆҰக͕ى͖ͳ͍
    type Query {
    node(id: ID!): Node!
    viewer: User!
    adminViewer: Admin!
    }

    View Slide

  12. 12
    (SBQI2-ͷϝϦοτ
    w σβΠϯͷϢʔεέʔεʹҾͬுΒΕͯɺ"1*Λมߋ͢Δඞཁ͕ͳ͍
    w ΫϥΠΞϯτʮը໘9ʹ:Λදࣔ͢Δඞཁ͕Ͱ͖ͨͷͰɺ9"1*Ͱ:ΛҾ͖͍ͨʯ
    w 3&45ύλʔϯΫϥΠΞϯτଆ͕ଥڠ͢Δ৔߹
    w όοΫΤϯυʮ9"1*ͷ੹຿͔Β:͸֎ΕΔͷͰ:"1*Λ࣮ߦͯ͠ཉ͍͠ʯ
    w ΫϥΠΞϯτʮ9"1*ͱ:"1*ճ࣮ߦ͢Δ͔ʜ355Ͱ69མͪ͠ίʔυ΋ෳࡶʹͳΔͳʯ
    w 3&45ύλʔϯόοΫΤϯυଆ͕ଥڠ͢Δ৔߹
    w όοΫΤϯυʮ9"1*Ͱ:ฦ͢Α͏ʹ͢Δ͔ʜ੹຿ͱͯ͠ҧ࿨ײ͋Δ͠ɺଞͷը໘Ͱ࢖Θͳ͍ͷ
    ʹ༨ܭͳ3FBE૿͑Δͳɻ΋͘͠͸ઐ༻ͷ9:"1*௥Ճ͢Δ͔ʜʯ
    w (SBQI2-ͷ৔߹ΫϥΠΞϯτόοΫΤϯυͲͪΒ΋޾ͤ
    w όοΫΤϯυʮը໘9༻ʹ9ͱ:ΛҾ͘2VFSZΛॻ͍ͯऔಘ͍͍ͯ͠Αଞͷը໘Ͱ͸9͚ͩΛҾ͘
    2VFSZॻ͚͹ຖճ:ΛҾ͘ඞཁͳ͍͔ΒύϑΥʔϚϯεͷ໰୊΋ͳ͍Αʯ
    w ΫϥΠΞϯτʮ355ͰࡁΉ͠ɺίʔυ͸γϯϓϧɺը໘Ͱඞཁͳ஋͢΂ͯऔಘͰ͖ͯخ͍͠ʯ
    w
    όοΫΤϯυΤϯδχΞͱͯ͠ͷ
    (SBQI2-࠷େͷϝϦοτ

    View Slide

  13. 6OJPOɺ*OUFSGBDF

    View Slide

  14. 14
    6OJPO
    union Announcement =
    TextAnnouncement |
    URLAnnouncement |
    ProductAnnouncement
    type TextAnnouncement {
    id: ID!
    text: String!
    }
    type URLAnnouncement {
    id: ID!
    url: String!
    }
    type ProductAnnouncement {
    id: ID!
    product: Product!
    }
    query Announcements {
    announcements {
    __typename
    ... on TextAnnouncement {
    id
    text
    }
    ... on URLAnnouncement {
    id
    url
    }
    ... on ProductAnnouncement {
    id
    product {
    id
    name
    price
    }
    }
    }
    }
    4DIFNB 2VFSZ
    type Product {
    id: ID!
    name: String!
    price: Int!
    }

    View Slide

  15. 15
    const Announcements: React.FC = () => {
    const { data } = useQuery(AnnouncementsDocument);
    return (

    Announcements

    {announcements.map((announcement) => {
    switch (announcement.__typename) {
    case "TextAnnouncement":
    return Text: {announcement.text};
    case "URLAnnouncement":
    return (

    URL: {announcement.url}

    );
    case "ProductAnnouncement":
    return Product: {announcement.product.name} - $
    {announcement.product.price};
    default:
    return null;
    }
    })}


    );
    };
    (SBQI2-Ͱ͸VOJPOͷߏ଄Λద੾ͳܕʹ
    ஋͕ೖͬͨঢ়ଶͰऔಘͰ͖Δ

    View Slide

  16. 16
    *OUFSGBDF
    interface Announcement {
    id: ID!
    title: String!
    }
    type TextAnnouncement implements Announcement {
    id: ID!
    title: String!
    text: String!
    }
    type URLAnnouncement implements Announcement {
    id: ID!
    title: String!
    url: String!
    }
    type ProductAnnouncement implements Announcement {
    id: ID!
    title: String!
    product: Product!
    }
    query Announcements {
    announcements {
    __typename
    id
    title
    ... on TextAnnouncement {
    text
    }
    ... on URLAnnouncement {
    url
    }
    ... on ProductAnnouncement {
    product {
    id
    name
    price
    }
    }
    }
    }
    4DIFNB 2VFSZ
    JEͱUJUMF͸JOUFSGBDFʹఆٛ͞Ε͍ͯΔͨΊ
    ܕʹΑΔ෼ذ͸ෆཁͰɺ
    ௚઀ར༻͢Δ͜ͱ͕Ͱ͖Δ

    View Slide

  17. 17
    const Announcements: React.FC = () => {
    const { data } = useQuery(AnnouncementsDocument);
    return (

    Announcements

    {announcements.map((announcement) => {
    const content = (() => {
    switch (announcement.__typename) {
    case "TextAnnouncement":
    return Text: {announcement.text};
    case "URLAnnouncement":
    return (

    URL: {announcement.url}

    );
    case "ProductAnnouncement":
    return Product: {announcement.product.name} - ${announcement.product.price};
    default:
    return null;
    }
    })();
    return (

    {announcement.title}
    {content}

    JEͱUJUMF͸JOUFSGBDFʹఆٛ͞Ε͍ͯΔͨΊ
    ܕʹΑΔ෼ذ͸ෆཁͰɺ
    ௚઀ར༻͢Δ͜ͱ͕Ͱ͖Δ

    View Slide

  18. 2VFSZͷ֦ு

    View Slide

  19. 19
    2VFSZ!EFGFSEJSFDUJWF
    query {
    person(id: "cGVvcGxlOjE=") {
    ...HomeWorldFragment @defer(label: "homeWorldDefer")
    name
    }
    }
    fragment HomeWorldFragment on Person {
    homeworld {
    name
    }
    }
    w !EFGFSEJSFDUJWF͸෇༩ͨ͠2VFSZͷ༏ઌ౓ΛԼ͛Δ
    w (SBQI2-͸ɺෳ਺ͷ஋Λಉ࣌ʹऔಘͰ͖Δ͕ɺ͢΂ͯͷ݁Ռ͕ἧΘͳ͍ͱϨεϙϯεΛ
    ฦ͞ͳ͍ɻ!EFGFSEJSFDUJWFΛઃఆ͢Δ͜ͱͰɺ४උ͕Ͱ͖ͨ'JFMEΛઌʹฦ͢͜ͱͰԠ
    ౴ੑ͕޲্͢Δɻ

    View Slide

  20. 20
    2VFSZ!TUSFBNEJSFDUJWF
    query {
    person(id: "cGVvcGxlOjE=") {
    name
    films @stream(initialCount: 2, label: "filmsStream") {
    title
    }
    }
    }
    w !TUSFBNEJSFDUJWF͸෇༩ͨ͠2VFSZͷ݁ՌΛετϦʔϜͰड͚औΔ
    w -JTUͷϨεϙϯεΛऔಘ͢Δࡍʹେ͖ͳ-JTUͷ৔߹ʹ!TUSFBNEJSFDUJWFΛࢦఆ͢Δͱ
    JJUJBM$PVOUͰઃఆͨ͠਺ͷϨεϙϯεΛઌʹड͚औΔ͜ͱͰԠ౴ੑΛ্͛Δɻ

    View Slide

  21. 3FMBZ(SBQI2-4FSWFS4QFDJ
    fi
    DBUJPO

    View Slide

  22. 22
    (SBQI2-4FSWFS4QFDJ
    fi
    DBUJPO/PEFJOUFSGBDF
    type Query {
    node(id: ID!): Node!
    }
    interface Node {
    id: ID! @goField(forceResolver: true)
    }
    query GetProduct($productID: ID!) {
    node(id: $productID) {
    ... on Product {
    id
    name
    }
    }
    }
    w *%Λ࣋ͭΦϒδΣΫτ͸/PEFJOUFSGBDFΛ࣮૷͢Δɻ
    w ͜ΕΛ࣮૷͢ΔͨΊʹ͸ɺ*%͔ΒͲͷΦϒδΣΫτ͔Λ൑ผͰ͖Δඞཁ͕͋Γ·͢ɻ
    w (MPCBM0CKFDU*EFOUJ
    fi
    DBUJPO
    w 4IPQJGZͳͲ͸ҎԼͷΑ͏ͳ*%ܗࣜΛ࠾༻͍ͯ͠·͢ɻHJETIPQJGZ1SPEVDU

    View Slide

  23. 23
    (SBQI2-$VSTPS$POOFDUJPOT4QFDJ
    fi
    DBUJPO
    w $POOFDUJPO
    w -JTUܗࣜͷ஋ΛϖʔδωʔγϣϯͰऔಘ͢Δඞཁ͕͋Δ৔߹ͷ4DIFNBఆٛͷఏҊ
    w 999$POOFDUJPOUZQFΛఆٛ
    w FEHFTPSOPEFTϑΟʔϧυΛ࣋ͭ
    w QBHF*OGPΛ࣋ͭ
    w FEHFTͷ৔߹͸ɺ999&EHFΛఆٛ
    w /PEFϑΟʔϧυΛ࣋ͭ
    w 1BHF*OGPUZQFΛఆٛ
    w Χʔιϧͷ։࢝ͱऴྃ஍఺ͷ৘ใ
    w લޙͷϖʔδωʔγϣϯͷଘࡏͷ༗ແ
    type UserConnection {
    pageInfo: PageInfo!
    edges: [UserEdge!]!
    nodes: [User!]
    }
    type UserEdge {
    node: User!
    }
    type PageInfo {
    startCursor: String!
    endCursor: String!
    hasNextPage: Boolean!
    hasPreviousPage: Boolean!
    }

    View Slide

  24. ϑΥʔϚοτ

    View Slide

  25. 25
    2VFSZɺ.VUBUJPOͷϑΥʔϚοτ
    w 3FRVFTU
    w 1045ͷ#PEZ
    {


    "query": "...",


    "operationName": "...",


    "variables": { "myVariable": "someValue", ... }


    }


    {


    "data": { ... },


    "errors": [ ... ]


    }


    w 3FTQPOTF
    w #PEZ

    View Slide

  26. 26
    2VFSZɺ.VUBUJPOͷϑΥʔϚοτ
    w 3FRVFTU
    w (&5ͷRVFSZQBSBNFUFS
    http://myapi/graphql?query={me{name}}
    w 1FSTJTUFE2VFSJFT
    w 2VFSZΛ4)"ͰϋογϡԽͨ͠΋ͷΛૹ৴
    w ϦΫΤεταΠζΛখ͘͞Ͱ͖Δ
    w (&5ͱར༻͢Δ͜ͱͰ$%/ͰΩϟογϡՄೳʹͳΔ
    w ొ࿥͞Ε͍ͯΔϋογϡͷΈΛ࣮ߦՄೳʹ͢Δ͜ͱ΋Ͱ͖Δ

    View Slide

  27. 27
    4FSJBMJ[BUJPO'PSNBU
    w (SBQI2-ͷ4FSJBMJ[BUJPO'PSNBU͸+40/ʹݶఆ͞Ε͍ͯͳ͍
    w ݱ࣮తͳੈͷதͷ࣮૷ͱͯ͠͸େ൒͕+40/
    w .645
    w .BQɺ-JTUɺ4USJOHɺ/VMM
    w 4)06-%
    w #PPMFBOɺ*OUɺ'MPBUɺ&OVN
    w ରԠ͍ͯ͠ͳ͍৔߹͸4USJOHͱͯ͠ૹΒΕΔ
    (SBQI2-7BMVF +40/7BMVF
    .BQ 0CKFDU
    -JTU "SSBZ
    /VMM OVMM
    4USJOH 4USJOH
    #PPMFBO USVFPSGBMTF
    *OU /VNCFS
    'MPBU /VNCFS
    &OVN7BMVF 4USJOH

    View Slide

  28. 4DIFNB؅ཧ

    View Slide

  29. 29
    4DIFNBͷ؅ཧ
    w (SBQI2-2VFSZͷิ׬΍ίʔυੜ੒ʹ4DIFNB͕ඞཁ
    w 4DIFNBͷ৘ใΛ֤छπʔϧ͕௚઀ΤϯυϙΠϯτ͔Β*OUSPTQFDUJPO2VFSZʹΑΓ
    औಘ
    w 4DIFNBϑΝΠϧΛΤϯυϙΠϯτ͔Β*OUSPTQFDUJPO2VFSZʹΑΓμ΢ϯϩʔυ͠
    ͯɺμ΢ϯϩʔυͨ͠ϑΝΠϧΛ֤छπʔϧ͕ಡΈࠐΉ
    w 4DIFNBϑΝΠϧΛϦϙδτϦ͔Βμ΢ϯϩʔυͯ͠ɺμ΢ϯϩʔυͨ͠ϑΝΠϧ
    Λ֤छπʔϧ͕ಡΈࠐΉ
    w *OUSPTQFDUJPO2VFSZΛར༻ͨ͠εΩʔϚμ΢ϯϩʔυπʔϧ
    w IUUQTHJUIVCDPNHRMHPHFUHSBQIRMTDIFNB

    View Slide

  30. "SDIJUFDUVSF

    View Slide

  31. 31
    GBDUPSBQQ
    w &WFOU4PVSDJOHɺ$234ɺ6OJEJSFDUJPOBM%BUB
    fl
    PXʹӨڹΛड͚͍ͯΔ
    w .VUBUJPO͸ΠϕϯτΛه࿥ͯ͠ଈ࠲ʹϨεϙϯεΛฦͨ͢ΊԠ౴ੑ͕ߴ͍
    w .VUBUJPOͷ݁ՌΛ଴ͪड͚ͯɺར༻͢ΔͷͰ͸ͳ͘.VUBUJPOʹΑΔมߋ͸4VCTDSJQUJPO
    Λհͯ͠औಘ͢Δ୯ํ޲σʔλϑϩʔ
    w .VUBUJPOΠϕϯτͷอଘ৽͍͠ঢ়ଶͷ࡞੒4VCTDSJQUJPOͰ௨஌

    View Slide

  32. 32
    'FEFSBUJPO "QPMMP'FEFSBUJPO
    ɺ4DIFNB4UJUDIJOH
    w ෳ਺ͷ(SBQI2-"1*Λ૊Έ߹ΘͤͨεʔύʔάϥϑΛ࡞੒͢ΔͨΊͷΞʔΩςΫνϟ
    w େ͖ͳձࣾͰ͸ɺ(SBQI2-͸(BUFXBZͱͯ͠ѻΘΕͯɺഎޙͷ.JDSPTFSWJDFT΁ͷϦΫ
    ΤετΛ؅ཧ͢Δɻ
    w .JDSPTFSWJDFT͸νʔϜ୯ҐͰ෼ׂ͞Ε͍ͯΔ͕ɺ(SBQI2-ͷ(BUFXBZ͸ͭͳͷͰଟ͘
    ͷνʔϜ͕มߋ͢ΔͨΊσϓϩΠͷͰಠཱੑͳͲ͕೉͘͠ͳΔɻ
    w ͦ͜Ͱ'FEFSBUJPOΛ࢖͏͜ͱͰɺ(SBQI2-Λ෼ׂ͢Δɻ

    View Slide

  33. 33
    'FEFSBUJPO "QPMMP'FEFSBUJPO

    .JDSPTFSWJDFT
    (BUFXBZ
    (BUFXBZͷ෼ׂ

    View Slide

  34. 1SPUPDPM#V
    ff
    FSTPO)551ͱͷൺֱ

    View Slide

  35. 35
    1SPUPDPM#V
    ff
    FSTPO)551ͱͷൺֱ
    w εΩʔϚͷදݱ
    w (SBQIߏ଄ΛදݱͰ͖Δ
    w 6OJPOɺ*OUFSGBDF͕ඪ४Ͱ࣋ͭͨΊදݱ͠΍͍͢
    w SFTPMWFSʹΑΓϑΟʔϧυͷ௥Ճ͕͠΍͍͢
    w طଘͷ2VFSZ͸औಘ͍ͯ͠ͳ͍ͷͰӨڹ͠ͳ͍
    w ϑΟʔϧυͷ࡟আͷ͠΍͢͞͸มΘΒͳ͍
    w εΩʔϚ؅ཧ
    w *OUSPTQFDUJPO2VFSZʹΑΓΤϯυϙΠϯτ͔Βμ΢ϯϩʔυPS௚઀ͷࢀর͕Ͱ͖Δ
    w ੩తղੳπʔϧͷ࣮૷ͷ͠΍͢͞
    w ࣮૷͍ͨ͠ݴޠͷ"451BSTFS͕͋Ε͹ࠩ͸ͳ͍

    View Slide

  36. 36
    1SPUPDPM#V
    ff
    FSTPO)551ͱͷൺֱ
    w ಠ࣮ࣗ૷ͷ௥Ճ
    w (SBQI2-EJSFDUJWF
    w 1SPUPDPM#V
    ff
    FST1MVHJO
    w (SBQI2-ͷEJSFDUJWFͷํ͕ؾܰʹ௥ՃͰ͖Δ
    w .JDSPTFSWJDFT
    w .JDSPTFSWJDFTؒΛ(SBQI2-Ͱ࣮૷͢ΔͱεΩʔϚઃܭʹۤ࿑͢Δ
    w .JDSPTFSWJDFT͸H31$ͷࢿ࢈͕ଟ͍
    w (SBQI2-͸(BUFXBZʹݶఆͨ͠ํ͕ߟ͑ํ͸γϯϓϧ
    w 'FEFSBUJPO͸(BUFXBZͷ෼ׂʹ׆༻͢Δ

    View Slide

  37. 37
    1SPUPDPM#V
    ff
    FSTPO)551ͱͷൺֱ
    w ϓϩτίϧ΁ͷґଘ
    w (SBQI2-͸ྑ͘΋ѱ͘΋ଟ͘ͷϓϥΫςΟε͕࢓༷΍ϑϨʔϜϫʔΫʹଘࡏ͢ΔͨΊґ
    ଘ͕ڧ͍
    w 1SPUPDPM#V
    ff
    FSTPO)551͸γϯϓϧͰ͋Γɺଞͷྨࣅϓϩίτϧ΁ͷҠߦ΋༰қ
    w Ϋϥ΢υαʔϏεͷରԠ
    w 8FCαʔϏε͕3&45΍+40/Λத৺ʹൃల͖ͯͨ͠ͷͰɺ(SBQI2-ͱϚον͠ͳ͍͜ͱ
    ͕ଟʑ͋Δ
    w ྫ
    ϦΫΤετͷղੳΛΤϯυϙΠϯτ͝ͱʹ΍͍ͬͯΔͱɺ͢΂ͯͷϦΫΤετΛ
    HSBQIRMΤϯυϙΠϯτʹ౤͛Δ(SBQI2-Ͱ͸෼ੳ͕೉͍͠
    w (SBQI2-ઐ༻αʔϏεͷར༻΍ɺطଘαʔϏεͷ্Ͱ(SBQI2-Λద༻ͤ͞ΔͨΊͷ޻෉
    ͕ඞཁʹͳΔ
    w ΞϓϦ
    w "QPMMPJ04ɺ"QPMMP,PUMJO͕ൃల్্
    w (SBQI2-͸1SPUPDPM#V
    ff
    FSTΑΓϥΠϒϥϦͷӨڹ͕େ͖͍

    View Slide

  38. 38
    1SPUPDPM#V
    ff
    FSTPO)551ͱͷൺֱ
    w Ҏ্ͷ͜ͱ͔Βɺ(SBQI2-Λར༻͢Δࡍʹ͸ɺ(SBQI2-ʹϕοτͯ͠ɺ৔߹ʹΑͬͯ͸
    ࣗ෼ͨͪͰղܾ͢Δ࢟੎͕ඞཁʹͳΔ

    View Slide

  39. ·ͱΊ

    View Slide

  40. 40
    ެࣜɾඇެࣜͷ੔ཧ
    w (SBQI2-4QFD
    w 2VFSZɺ.VUBUJPOɺ4VCTDSJQUJPO
    w (SBQI2-4QFD8PSLJOH%SBGU
    w !EFGFSɺ!TUSFBN
    w 3FMBZ
    w /PEF
    w $POOFDUJPO
    w "QPMMP
    w 'FEFSBUJPO
    w 5IF(6*-%
    w 4DIFNB4UJUIJOH
    w )BTVSB
    w GBDUPS"QQ
    w

    View Slide