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

フロントエンドのパラダイムを参考にバックエンド開発を再考する / TypeScript による GraphQL バックエンド開発

Naoya Ito
October 01, 2022

フロントエンドのパラダイムを参考にバックエンド開発を再考する / TypeScript による GraphQL バックエンド開発

2022年10月1日に開催された #postdev での発表です

Naoya Ito

October 01, 2022
Tweet

More Decks by Naoya Ito

Other Decks in Technology

Transcript

  1. ϑϩϯτΤϯυͷύϥμΠϜΛࢀߟʹόοΫΤϯυ։ൃΛ࠶ߟ͢Δ 5ZQF4DSJQUʹΑΔ (SBQI2-όοΫΤϯυ։ൃ גࣜձࣾ Ұٳ ҏ౻ ௚໵

  2. K2VFSZظ .1"XJUIK2VFSZ ʮ+BWB4DSJQU͕ಘҙʯͳਓ͸͍͕ͨɺϑϩϯτΤϯυ όοΫΤϯυͱ͍͏໾ׂ෼୺͸ߦΘ Εͣɺશһ͕ಉ͡ྖҬΛ୲౰͍ͯͨ͠

  3. 7VFKTಋೖظ .1" 7VFϑϩϯτΤϯυ ʮϑϩϯτΤϯυΤϯδχΞʯʮόοΫΤϯυΤϯδχΞʯͱ͍͏໾ׂ෼୺͕গͣͭ͠ͳ͞Ε ΔΑ͏ʹ

  4. /VYU࣌୅ /VYU (SBQI2-όοΫΤϯυ ʮϑϩϯτΤϯυʯʮόοΫΤϯυʯͷ໾ׂ෼୺͕໌֬ʹ ϑϩϯτΤϯυͷΈ։ൃΛ͢Δɺͱ͍͏୲౰ऀ΋

  5. ૒ํͷٕज़తؔ৺ࣄʹΪϟοϓ • ΞϓϦέʔγϣϯͷঢ়ଶ؅ཧϞσϧ • σβΠϯγεςϜ • ϓϦϨϯμϦϯά • ŋŋŋ ϑϩϯτΤϯυ

    όοΫΤϯυ • υϝΠϯϞσϧ • ϨΠϠʔυɾΞʔΩςΫνϟ • $234 • ŋŋŋ ૊৫ͷٕज़࿅౓্͕͕Ε͹্͕Δ΄Ͳɺؔ৺ࣄͷΪϟοϓ͕޿͕͍ͬͯͬͨ
  6. ͖͔͚ͬ • ৽نϓϩδΣΫτ্ཱ͕͕ͪΔ • ϑϩϯτΤϯυ͸ 3FBDU3FMBZ3FDPJM Λ࠾༻ • (SBQI2-όοΫΤϯυ ŋŋŋ

    ௕Β͘ 1ZUIPOͰɺΫϥεΛଟ༻ͨ͠ΫϦʔϯΞʔΩςΫνϟ తͳઃܭͰ΍͖͕ͬͯͨɺࠓճ͸Ͳ͏͢Δ͔
  7. ৽نϓϩμΫτ͸খ͞ͳνʔϜͰີ౓ߴٞ͘࿦ͯ͠࡞Γ͍ͨ • ސ٬ͷͲΜͳ໰୊ΛɺͲ͏ղܾ͍͔ͨ͠ ŋŋŋ σΟεΧογϣϯΛେࣄʹ͍ͨ͠ – ϑϩϯΤϯυɺόοΫΤϯυؔ܎ͳ͘ର৅υϝΠϯྖҬʹৄ͘͠ͳΓ͍ͨ – ͲΜͳମݧΛɺͲΜͳ 6*ͰɺͲ͏͍͏ϞσϧͰɺͲΜͳσʔλઃܭΛ࣮ͯ͠ݱ͢Δͷ͔ɻॳظ

    ϑΣʔζͰ͸શһ͕ͳΔ΂͘޿͘஌͓͖͍ͬͯͨ • ૣظʹ໾ׂ෼୺Λߦ͍͗͢Δͱɺؔ৺ࣄͷ෼அΛ༠ൃͯ͠͠·͏
  8. 3FBDUͰϑϩϯτΤϯυΛ։ൃ͔ͯ͠Βɺ όοΫΤϯυΛॻ͘ͱŋŋŋ • 3FBDUŋŋŋ খ͞ͳؔ਺Λ૊Έ߹Θͤͯએݴతʹॻ͍͍ͯ͘ • όοΫΤϯυ ŋŋŋ ΫϥεΛͨ͘͞Μॻ͍ͯɺϨΠϠʔΛލ͙ͱ %50Ͱͷ஋٧Ίସ͑Λߦͬ

    ͯɺJOUFSGBDFͰґଘੑͷٯసΛߦͬͯŋŋŋ – ʮŋŋŋϑϩϯτΤϯυͩͱ͜͏͍͏͜ͱɺ͋Μ·Γ΍Βͳ͍ΑͶʯ ։ൃ࣌ͷϝϯλϧϞσϧͷΪϟοϓ͕େ͖͍ ίϯςΩετεΠονͷෛ୲΋େ͖͍
  9. όοΫΤϯυ։ൃͷ΍ΓํΛ࠶ߟͯ͠Έ͍ͨ • 3FBDUΛ࢖͍ͬͯΔͱϑϩϯτΤϯυ͸ബ͘ॻ͘͜ͱ͕Ͱ͖Δ • ؔ৺ࣄ͕ҧ͏ͷ͸౰વɻ͔ͱ͍ͬͯɺ΍Γํ͕ҧ͏ͷΛશٙ͘Θͳ͍ͷ΋Ͳ͏ͩΖ͏ ϑϩϯτΤϯυͷঢ়ଶ؅ཧ͸ෳࡶ ͦͷෳࡶͳ΋ͷΛͲ͏ѻ͏͔ɺݱ࣌఺Ͱ࠷ྑͷϞσϧͷͻͱ͕ͭ 3FBDUͷ͸ͣ ʮෳࡶͳঢ়ଶΛͲ͏ѻ͏͔ʯͱ͍͏؍఺ͰɺαʔόʔαΠυ΋ಉ͡Α͏ʹߟ͑ΒΕͳ͍ͷ͔ Α͠ɺ(SBQI2-όοΫΤϯ

    υ΋ 5ZQF4DSJQUͰॻ͍ͯΈ Α͏
  10. վΊͯɺࡢࠓͷϑϩϯτΤϯυͷϓϩάϥϛϯάύϥμΠϜΛߟ͑ͯΈΔ https://zenn.dev/mizchi/articles/oop-think-modern

  11. &MNΞʔΩςΫνϟ https://guide.elm-lang.jp/architecture/

  12. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of ToggleLike -> ( { model | photo = Maybe.map toggleLike model.photo }, Cmd.none ) UpdateComment comment -> ( { model | photo = Maybe.map (updateComment comment) model.photo }, Cmd.none ) SaveComment -> ( { model | photo = Maybe.map saveNewComment model.photo }, Cmd.none ) LoadFeed (Ok photo) -> ( { model | photo = Just photo }, Cmd.none ) LoadFeed (Err _) -> ( model, Cmd.none ) viewLikeButton : Photo -> Html Msg viewLikeButton model = let buttonClass = if model.liked then ... div [ class "like-button" ] [ i [ class "fa fa-2x", class buttonClass, onClick ToggleLike ] [] ] &MN 7JFX͸ .PEFMΛඳըɻ Ϣʔβʔૢ࡞ʹԠͯ͡Πϕ ϯτΛૹΔͱŋŋŋ &MNϥϯλΠϜ͕ VQEBUFؔ਺ ΛݺͿɻؔ਺ʹ͸Πϕϯτͷछ ྨʹԠͨ͡Ϟσϧͷঢ়ଶભҠΛ هड़͓ͯ͘͠
  13. ঢ়ଶભҠͷؔ਺ ֎քͱ΍Γͱ Γ *0 イベント コマンド

  14. model -> model' model -> model' model -> model' ΠϕϯτΛܖػʹঢ়ଶ͕ભҠ͢Δ

    ŋŋŋ ࣌ܥྻʹج͍ͮͨঢ়ଶ
  15. ঢ়ଶભҠͷؔ਺ ϥϯλΠϜ΍ϑ ϨʔϜϫʔΫ イベント コマンド Πϕϯτʹ൐͍ঢ়ଶΛભҠͤͯ͞ɺ͋ͱ͸ϑϨʔϜϫʔΫ΍ϥϯλΠϜʹ೚ͤΔ

  16. 3FEVY"QQMJDBUJPO%BUB'MPX https://redux.js.org/tutorials/essentials/part-1-overview-concepts

  17. 3FDPJM function TextInput() { const [text, setText] = useRecoilState(textState); const

    onChange = (event) => { setText(event.target.value); }; return ( <div> <input type="text" value={text} onChange={onChange} /> <br /> Echo: {text} </div> ); }
  18. 3FDPJM • 3FEVYʹΑΔେ͖ͳάϩʔόϧεςʔτ͸ɺѻ͍ͮΒ͍ہ໘΋͋ͬͨ • ΑΓείʔϓΛڱ͘ɺখ͘͞ɺϩʔΧϧεςʔτ VTF4UBUF ಉ༷ʹϑοΫͰએݴతʹѻ͍ ͍ͨ &MNΞʔΩςΫνϟ΍ 3FEVYͰൃݟ͞Εͨྑ͍ϓϥΫςΟε͸౿ऻͭͭ͠ɺͰ͖Δݶ

    Γখ͞ͳείʔϓͰঢ়ଶΛѻ͍͚ͬͯΔͱྑͦ͞͏
  19. None
  20. όοΫΤϯυͰ΋ಉ͡Α͏ʹʮ࣌ܥྻʹجͮ͘ঢ়ଶભҠʯͷࢹ఺Ͱߟ͑ΒΕͳ͍͔ • όοΫΤϯυͷੈքͷओͳʮঢ়ଶʯ ŋŋŋ υϝΠϯϞσϧͷঢ়ଶ • υϝΠϯϞσϧͷঢ়ଶΛભҠͤ͞ΔΠϕϯτ ŋŋŋ υϝΠϯΠϕϯτ

  21. ͨͱ͑͹ʮ॓ധ༧໿ʯΛྫʹυϝΠϯϞσϧΛվΊͯߟ͑ͯΈΔ • ͲΜͳ؍఺ʹ஫໨ͯ͠ߟ͑ͯΈΔ΂͖͔ – σʔλߏ଄ – &3ਤ – Ϋϥεͷ࣮૷ –

    ը໘ • ͍ͣΕ΋੩తͳߏ଄ʹয఺Λ౰͍ͯͯΔɻࢹ఺Λม͑ͯΈ͍ͨ – ಈతͳ΋ͷŋŋŋυϝΠϯΠϕϯτ΍ঢ়ଶʹয఺Λ౰ͯͯΈΔͱ
  22. ʮ༧໿ʯͷঢ়ଶભҠʹண໨ͯ͠ΈΔ ༧໿׬ྃ Χʔυܾࡁ ࡁΈ Ωϟϯηϧ ॓ധࡁΈ

  23. ৽ن༧໿͕׬ྃ͢Δલ͔ΒυϝΠϯϞσϧ͸ଘࡏ͍ͯ͠Δ ༧໿׬ྃ Ωϟϯηϧ ॓ധࡁΈ ೖྗ ݕূࡁΈ ೖྗະݕূ ࡏݿ֬อ ࡁΈ

  24. ঢ়ଶ͸Կ͔͠ΒͷΠϕϯτΛܖػʹભҠ͢Δ ༧໿׬ྃ Ωϟϯηϧ ॓ധࡁΈ ೖྗ ݕূࡁΈ ೖྗະݕূ ࡏݿ֬อ ࡁΈ ༧໿Λ։࢝ͨ͠

    ݕূ͕׬ྃͨ͠ ࡏݿΛ֬อͨ͠ ༧໿Λߦͬͨ Ωϟϯηϧ͞Εͨ ॓ധͨ͠
  25. model -> model' model -> model' model -> model' ͓΍

  26. ྆୺͸֎෦ͱͷΠϯλϑΣʔε model -> model model -> model &WFOU)BOEMFS 8FC"QQͳΒ SPVUFS

    %#ʹอଘ͠ Ϩεϙϯε 6*
  27. model -> model model -> model event &WFOU)BOEMFS 8FC"QQͳΒ SPVUFS

    %#ʹอଘ͠ Ϩεϙϯε 6* event event ֎ͷੈք ֎ͷੈք ֎ͷੈք Πϕϯτ ˠϞσϧͷঢ়ଶભҠ 🤔 Ͳ͔͜Ͱݟͨͳŋŋŋ
  28. ঢ়ଶભҠͷؔ਺ ϥϯλΠϜ΍ϑ ϨʔϜϫʔΫ イベント コマンド ಉ͡

  29. *0ঢ়ଶભҠ *0 Pure function Model -> Model *0 JOQVUMPBE *0

    PVUQVU
  30. None
  31. https://www.slideshare.net/ScottWlaschin/reinventing-the-transaction-script-ndc-london-2020

  32. ϑϩϯτΤϯυͱόοΫΤϯυͷঢ়ଶ؅ཧ • ϑϩϯτΤϯυͷঢ়ଶ؅ཧ ŋŋŋ ओͳؔ৺ࣄ͸ʮΞϓϦέʔγϣϯͷঢ়ଶʯ • όοΫΤϯυͷঢ়ଶ؅ཧ ŋŋŋ ओͳؔ৺ࣄ͸ʮυϝΠϯϞσϧɺυϝΠϯΦϒδΣΫτͷঢ় ଶʯ

    ؅ཧ͍ͯ͠Δঢ়ଶͷίϯςΩετ͸ҧ͏΋ͷͷ ঢ়ଶ؅ཧͷϞσϧ͸ࣅͨΑ͏ʹߟ͑ΒΕΔͷͰ͸ͳ͍͔
  33. ʮυϝΠϯΦϒδΣΫτͷঢ়ଶભҠΛએݴతʹهड़ͭͭ͠ *0͔Β෼཭͢Δʯ • ͜ͷίϯηϓτͰ࣮૷ • &MNΞʔΩςΫνϟ͓Αͼ '%%%ຊΛࢀߟʹ

  34. ͜ͷελΠϧͰΑ͘࢖͏΋ͷ • type / interface • λά෇͖ϢχΦϯ ௚࿨ܕ • 3FTVMUܕ

    • ΧϦʔԽ • ܕͷϒϥϯυԽ ίϯύχΦϯΦϒδΣΫτ
  35. ͋·Γ࢖Θͳ͍΋ͷ • class • ྫ֎ͷ throw – Error Ϋϥε͸࢖͍·͢

  36. ؆୯ͳϢʔεέʔεྫ

  37. 5BHΤϯςΟςΟ ू໿ϧʔτ ͷঢ়ଶભҠʹண໨͢Δ 7BMJEBUFE 6OWBMJEBUFE $SFBUFE ೖྗ͕͋ͬͨ ݕূͨ͠ ࡞੒ͨ͠ ˞͜͜Ͱͷʮ$SFBUFEʯ͸͋͘·ͰυϝΠϯΦϒδΣΫτ͕

    l࡞੒ࡁΈzʹͳͬͨঢ়ଶͰ͋ͬͯɺσʔλϕʔεʹϨίʔυ Λ௥Ճͨ͠ɺͱ͍͏ঢ়ଶͰ͸ͳ͍
  38. 5BHΫϥεΛ࡞Δ export class Tag { state: 'Unvalidated' | 'Validated' |

    'Created', id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined }
  39. export class Tag { state: 'Unvalidated' | 'Validated' | 'Created',

    id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined } ঢ়ଶભҠલʹ͸֬ఆ͠ͳ͍ϓϩύςΟ͕ VOEFGJOFE ʹͳͬͯ͠·͏ŋŋŋ
  40. interface UnvalidatedTag { kind: 'Unvalidated' groupId: string label: string icon?:

    { symbol: string; type: TagIconType; color?: string | null | undefined } | null | undefined } interface ValidatedTag { kind: 'Validated' groupId: RestaurantGroupId label: string icon: TagIcon } export interface CreatedTag { kind: 'Created' id: TagId groupId: RestaurantGroupId label: TagLabel icon: TagIcon sortOrder: number builtin: boolean } //※この型は実際には出番がないので使っていない export type Tag = UnvalidatedTag | ValidatedTag | CreatedTag ͦ͜Ͱঢ়ଶ͝ͱʹܕΛఆٛ͢Δ ঢ়ଶ͕ભҠ͢Δ υϝΠϯΠϕ ϯτ͕ൃੜ͢Δ͝ͱʹϞσϧͷ஋ ͕֬ఆ͍ͯ͘͠ͷ͕એݴͰ͖͍ͯ Δ
  41. l.BLF*MMFHBM4UBUFT6OSFQSFTFOUBCMFz interface User { memberId: MemberId | undefined guestId: GuestId

    | undefined } interface Member { userId: MemberId } interface Guest { guestId: GuestId } type User = Member | Guest औΓಘΔ஋ͷछྨ਺͸֤ଐੑͷੵʹͳΔ ௚ੵ Y ɾ྆ํ VOEFGJOFE ɾ྆ํͷ஋͕ຒ·Δ ͱ͍͏࢓্༷͋Γಘͳ͍ঢ়ଶ͕ੜ·ΕΔ औΓಘΔछྨ਺͸֤ଐੑͷ࿨ ௚࿨   ࢓্༷͋Γಘͳ͍ঢ়ଶ͸දݱ͠ͳ͍ Ϩίʔυ͸ʮ͔ͭ "/% ʯ ϢχΦϯ͸ʮ·ͨ͸ 03 ʯ
  42. ͪ͜ΒΑΓ΋ŋŋŋ export class Tag { state: 'Unvalidated' | 'Validated' |

    'Created', id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined }
  43. interface UnvalidatedTag { kind: 'Unvalidated' groupId: string label: string icon?:

    { symbol: string; type: TagIconType; color?: string | null | undefined } | null | undefined } interface ValidatedTag { kind: 'Validated' groupId: RestaurantGroupId label: string icon: TagIcon } export interface CreatedTag { kind: 'Created' id: TagId groupId: RestaurantGroupId label: TagLabel icon: TagIcon sortOrder: number builtin: boolean } export type Tag = UnvalidatedTag | ValidatedTag | CreatedTag ͪ͜Βͷํ͕ɺ஋ͷ૊Έ߹Θͤύλʔϯ͕গͳ͘ݫີ
  44. type validateTag = (model: UnvalidatedTag) => ValidatedTag const validateTag: validateTag

    = (model) => { // (省略: 値の validation ...) return { ...model, kind: 'Validated', groupId: RestaurantGroupId(model.groupId), icon: model.icon ? TagIcon(model.icon) : NoIcon(), } } ঢ়ଶΛભҠͤ͞Δεςοϓ ؔ਺ 
  45. ঢ়ଶΛભҠͤ͞Δεςοϓ ؔ਺  type createTag = (model: ValidatedTag) => CreatedTag

    const createTag: CreatedTag = (model) => { return { ...model, kind: 'Created', id: generateTagId(), sortOrder: getTagSortOrder({ groupId: model.groupId }), builtin: false, } } ४උ͕੔ͬͯॳΊͯ஋͕֬ఆ ͢ΔͷΛࣗવʹهड़Ͱ͖Δ ͳ͓ getTagSortOrder ͸ *0͕͋Δ ͨΊ %*͍ͯ͠Δɻࠓճ͸ׂѪ
  46. Ϟσϧͷܕɺؔ਺ͷܕʹΑͬͯঢ়ଶભҠΛએݴతʹهड़͢Δ 7BMJEBUFE 6OWBMJEBUFE $SFBUFE (model: UnvalidatedTag) => ValidatedTag (model: ValidatedTag)

    => CreatedTag
  47. ݸผʹఆٛͨ͠ঢ়ଶભҠͷؔ਺Λܨ͍͛ͨ • Ͱ΋ɺܭࢉ͸ʮ్தͰࣦഊ͢ΔʯՄೳੑ͕͋Δ – ͨͱ͑͹υϝΠϯϞσϧͷࣄલ৚݅Λຬͨ͞ͳ͍Τϥʔ – 7BMJEBUJPO&SSPSŋŋŋ – .BY5BH-JNJU&YDFFEFEʜ •

    ʮ్தͰࣦഊ͢Δʯ͜ͱΛܕͰએݴͰ͖ͳ͍͔ ˠ 3FTVMUܕ
  48. 3FTVMUܕͰࣦഊͷՄೳੑͷ͋ΔܭࢉΛҰຊಓʹ߹੒͢Δ import { Result, ok, err } from 'neverthrow' function

    itsUnder100(n: number): Result<number, Error> { return n <= 100 ? ok(n) : err(new Error('100より大きい数字です')) } function itsEven(n: number): Result<number, Error> { return n % 2 == 0 ? ok(n) : err(new Error('奇数です')) } function itsPositive(n: number): Result<number, Error> { return n > 0 ? ok(n) : err(new Error('負数です')) } const result = ok(96).andThen(itsUnder100).andThen(itsEven).andThen(itsPositive) result.match( (n) => console.log(n), (error) => { throw error } )
  49. 3FTVMUܕͰঢ়ଶભҠؔ਺Λͭͳ͛ͯɺҰͭͷʮϫʔΫϑϩʔʯΛ࡞Δ 7BMJEBUFE5BH  $SFBUFE5BH 6OWBMJEBUFE5BH  7BMJEBUFE5BH 8PSL'MPX

  50. type validateTag = (model: UnvalidatedTag) => Result<ValidatedTag, ValidationError> const validateTag:

    validateTag = (model) => { const groupId = RestaurantGroupId(model.groupId) const label = TagLabel(model.label) const icon = model.icon ? TagIcon(model.icon) : ok(NoIcon()) const values = Result.combine(tuple(groupId, label, icon)) return values.map(([groupId, label, icon]) => ({ ...model, kind: 'Validated', groupId, label, icon, })) } ঢ়ଶભҠͷ੒ޭ ࣦഊΛ 3FTVMUܕͰฦ͢Α͏ʹ͢Δ ஋ͷੜ੒ʹࣦഊ͢ΔՄೳੑ͕͋ΔͷͰɺ ͜ΕΒ΋ 3FTVMUܕΛฦ͢
  51. 3FTVMUܕͰঢ়ଶભҠؔ਺Λܨ͛ͯɺϫʔΫϑϩʔ υϝΠϯϩδοΫ Λ࡞Δ type WorkFlow = (model: UnvalidatedTag) => Result<CreatedTag,

    CreateTagError> export const createTagWorkFlow: WorkFlow = (model) => ok(model).andThen(validateTag).andThen(createTag)
  52. ϫʔΫϑϩʔͷ࢝·ΓͱऴΘΓ͕ɺ֎քͱͷ JOPVU 7BMJEBUFE5BH  $SFBUFE5BH 6OWBMJEBUFE5BH  7BMJEBUFE5BH 8PSL'MPX ೖྗͷ%50

    ྫ(SBQI2-*OQVU5ZQF Λ 6OWBMJEBUFE5BHʹม׵ UBH3FQPTJUPSZͰ $SFBUFE5BHΛอଘ
  53. (SBQI2-SFTPMWFS3FQPTJUPSZ %# ͱϫʔΫϑϩʔΛ઀ଓ͢Δ import { saveCreatedTag } from '../../../customers/repos/tagRepository' export

    const createTagMutation = mutationField('createTag', { ... resolve(_root, { input }, context) { const workflow = createTagWorkFlow() // GraphQL 入力をワークフローの入力に変換 const unvalidatedTag = toUnvalidatedTag({ ...input, groupId: context.operator.groupId, }) // ワークフローを実行し Repository パターンの関数で DB に保存 (ここも Result で繋ぐ) const result = ok(unvalidatedTag).andThen(workflow).andThen(saveCreatedTag(context)) return result.match( (tag) => ({ tag: { ...tag, id: toGlobalId('Tag', tag.id), }, }), (error) => { // ここで初めて例外をスロー (単に Nexus にエラーを伝える手段としてスローする) throw error } ) }, })
  54. ok(model).andThen(workflow).andThen(saveCreatedTag(context)) (SBQI2-*OQVU σʔλϕʔε Pure function Model -> Model *0 JOQVUMPBE

    *0 PVUQVU
  55. ঢ়ଶભҠͷؔ਺ ϥϯλΠϜ΍ϑ ϨʔϜϫʔΫ イベント コマンド

  56. σʔλϑϩʔϓϩάϥϛϯά • 3FTVMUܕͰࣦഊͷ͋ΔܭࢉΛ߹੒͠ɺσʔλͷ௨Γಓͱͯ͠ͷܭࢉաఔΛ࡞Δ – ͦ͜ʹσʔλΛ์ΓࠐΉͱɺͦͷதΛ௨ͬͯঢ়ଶભҠͨ͠σʔλ͕ಘΒΕΔ – σʔλΛσʔλͷ··ɺͦͷՄൖੑΛԼ͛ͣʹѻ͍͍ͨɻ݁Ռ class ͷొ৔ػձ͕ͳ͍ •

    ܭࢉΛҰຊಓʹ͢Δ – େҬ୤ग़͸͠ͳ͍ɻେҬ୤ग़͢Δͱܭࢉ͕ҰຊಓʹͳΒͳ͍ ˠྫ֎Λ࢖Θͳ͍ – ࣦഊͷ෼ذ͸ 3FTVMUͰ߹੒ ˞3FTVMUܕ͸Ϟφυ – ܭࢉ͕ҰຊಓʹͳΔ σʔλ͸ෆมɻೝ஌ෛՙ͕௿͘ͳΔ
  57. ϑϩϯτΤϯυͱͷൺֱ • ࣌ܥྻʹجͮ͘ঢ়ଶભҠ σʔλϑϩʔ Λએݴతʹهड़͢Δɺͱ͍͏ߟ͑ํ͸ಉ͡ʹͳͬͨ – ܕͱখ͞ͳؔ਺ͷએݴతͳهड़ͰɺϑϩʔΛ૊Έ্͛Δ • ҰํɺϫʔΫϑϩʔͷ࣮૷Λ͍ͯ͠Δͱ͖ͷײ֮ʹ͸·ͩڑ཭͕͋Δ –

    υϝΠϯΠϕϯτͰঢ়ଶભҠɺͱ͍ͬͯ΋ଟ͘ͷ৔߹͸ʮ7BMJEBUFͯ͠ɺೖྗͰυϝΠϯϞσϧ Λߋ৽͢Δʯ͚ͩ • ݁ՌɺϫʔΫϑϩʔ͸ఆܕతͳهड़͕ଟ͘ͳΔ ŋŋŋ ϑϨʔϜϫʔΫԽͰ͖Δ͔΋ • ϑϩϯτΤϯυ͸ͦ͜Λ 3FBDU΍ &MNͳͲͷϑϨʔϜϫʔΫ͕΍͍ͬͯΔ ͔ͩΒɺΠϕϯτʹର͢ΔϞ σϧͷঢ়ଶભҠͱɺͦͷঢ়ଶΛදݱ͢ΔϓϨθϯςʔγϣϯͷهड़ʹूதͰ͖Δ • ΠϕϯτͱΠϕϯτͷͭͳ͗߹Θͤ ྫ3FTVMUܕʹΑΔ߹੒ Λࣗ෼Ͱهड़͍ͯ͠Δͷ͕ݱঢ়
  58. ͕࣌ؒແ͍ͷͰɺ࣮૷ͷৄࡉ͸·ͨޙ೔ • ͜ͷ··Ͱ͸τϥϯβΫγϣϯεΫϦϓτͰɺڽूੑ͕௿͘ͳͬͯ͠·͏ • ΤϯςΟςΟܕͷपลʹɺίΞυϝΠϯϩδοΫͷؔ਺Λ࣮૷ͯ͠Ϟδϡʔϧ෼ׂ͢Δ • ϫʔΫϑϩʔ͸ͦͷίΞυϝΠϯϩδοΫΛ࢖ͬͯϑϩʔΛ૊ΈཱͯΔ໾ׂ͚ͩʹͳΔ – ΫϦʔϯɾΞʔΩςΫνϟͷ 6TF$BTFͱಉ͡

  59. ैདྷख๏ʹൺֱ͠هड़ྔ͸গͳ͍ • σʔλΛσʔλͷ··ӡΜͰ͍ΔͷͰʮ٧Ίସ͑ͯ໾ׂͷҟͳΔผछͷΦϒδΣΫτʯʹ͢ Δŋŋŋͱ͍͏ඞཁ͕ͳ͍ – ஋ͷίϐʔ͸࣮ࡍʹ͸ͱ͜ΖͲ͜Ζ΍͍ͬͯΔ͕ ͨͩͷσʔλΛ ෼ׂ୅ೖͰهड़Ͱ͖ΔͷͰɺ هड़ྔ͸࠷খ

  60. ݱ࣌఺Ͱͷײ૝ • ࢓্༷ͳ͍ঢ়ଶΛ࡞ΒͣʹࡁΉͨΊɺݎ࿚ • ΑΓෳࡶͳϫʔΫϑϩʔΛ࣮૷ͨ͠৔߹΋ಉ͡ߏ଄ʹऩ·Δɻೝ஌ෛՙ͕௿͍ • 3FTVMUܕʹΑΓܭࢉΛܨ͛ΒΕΔΑ͏هड़͢Δྑ͍ڧ੍ྗ͕ಇ͘ – ͨͩ͠ andThen().andThen().asyncAndThen.map()

    ͸͕͢͞ʹಡΈͮΒ͍ – )BTLFMMͷ EPه๏ɺ'ͷίϯϐϡςʔγϣϯࣜʹ૬౰͢Δ΋ͷ͕ཉ͍͠ŋŋŋ • ஋ͷ٧Ίସ͑ͷهड़͕ͳ͍ͷ͸ ͱͯ΋ خ͍͠
  61. ;Γ͔͑Γ https://zenn.dev/mizchi/articles/oop-think-modern

  62. ·ͱΊ • ࣌ܥྻʹجͮ͘ঢ়ଶભҠͷએݴͱϑϨʔϜϫʔΫଆʹΑΔঢ়ଶભҠɺௐఀ – ϑϩϯτΤϯυ͸ɺͦͷͨΊͷϑϨʔϜϫʔΫͷ࣮૷͕ॆ࣮͍ͯ͠Δ • ʮએݴతϓϩάϥϛϯάʯͷϓϥΫςΟε͸ɺ೥ݱ࣌఺Ͱ͸ܦݧతʹ΋ྑ͍΋ͷ – όοΫΤϯυ։ൃ΋͜ͷߟ͑ํʹऩᏑ͍ͤͯ͘͞ͷ΋ѱ͘ͳ͍ͷͰ͸ ˠ΍ͬͯΈͨΒ޷ײ৮

    – ྲྀߦΔ͔Ͳ͏͔͸Θ͔Γ·ͤΜ • ϑϩϯτΤϯυ όοΫΤϯυͷύϥμΠϜΪϟοϓΛগͳ͍͖͍ͯͨ͘͠
  63. ัଊ • 5ZQF4DSJQUʹ૊ΈࠐΈͷ 3FTVMUܕ͸ͳ͍ͷͰɺOFWFSUISPXΛ࢖ͬͨɻଞʹ΋ GQUTͳͲͷީิ͕͋Δ • 3FTVMUܕ͸ XPSLGMPXͷߏ੒͚ͩͰͳ͋͘ΒΏΔ৔ॴͰ࢖͏ • Ұํʮ3FTVMUܕύζϧʯʹ͸·Δ͕࣌͋Δŋŋŋ

    • ঺հ࢓੾Εͳ͔͕ͬͨ 1SPNJTF΋ 3FTVMU"TZODʹΑͬͯ 3FTVMUԽͯ͠߹੒Ͱ͖Δ • %PNBJO.PEFMJOH.BEF'VODUJPOBM ʹ͍ͭͯ – ॻ੶ͷఏҊख๏ͦͷ··ͩͱτϥϯβΫγϣϯεΫϦϓτʹͳΓڽूੑ͕௿͘ͳΔɻ ͨͩ͠ *0͸͖ͪΜͱ෼཭͞Ε͍ͯΔͷͰɺΑ Γྑ͍τϥϯβΫγϣϯεΫϦϓτͩͱ͸ࢥ͏ ΤϯςΟςΟͷܕͷपΓʹυϝΠϯϩδοΫͷؔ਺ΛूΊΔ͜ͱͰͦͷ໰୊͸ղফ Ͱ͖͍ͯΔ – ͋ΒΏΔϓϩύςΟʹܕΛఆٛ͢ΔυϝΠϯϓϦϛςΟϒతͳख๏Λ࠾͍ͬͯΔ͕ɺͦ͜͸࠾༻͠ͳ͔ͬͨɻ7BMVF0CKFDUͷΈ /PNJOBMͳܕఆٛΛܕͷϒϥϯυԽ ˞ΦϥΠϦʔͷ 5ZQF4DSJQUຊࢀর ʹΑͬͯߦ͍ͬͯΔ – 3FQPTJUPSZ͸ඞཁͳ͍ͱॻ͔Ε͍͕ͯͨɺू໿Λ͔ͤͬ͘ߏ੒͍ͯ͠ΔͷͰैདྷ௨Γ 3FQPTJUPSZύλʔϯͰू໿୯ҐͰͷӬଓԽ Λߦ͍ͬͯΔɻͳ͓ɺ3FQPTJUPSZͷத਎͸ 1SJTNBΛ࢖͍ͬͯΔ • UZQFͱ JOUFSGBDFͷ࢖͍෼͚ ŋŋŋ GQUTʹ฿͍ͬͯΔɻಛʹͦ͏͠ͳ͚Ε͹ͳΒ͍ཧ༝͸ͳ͍ͱࢥ͏ • ؔ਺͕σϑΥϧτͰΧϦʔԽ͞Εͳ͍ͷ͸࢓ํ͕ͳ͍ɻ࣌ંΧϦʔԽͨ͠ͷΛ๨Εͯ͸·Δ • SFBEPOMZ͸ԣணͯ͠ɺ࢖ͬͯͳ͍ɻ ͪΌΜͱݕ౼͍ͯ͠ͳ͍
  64. ࢀߟจݙ • 4DPUU8MBTDIJO ʮ%PNBJO.PEFMJOH.BEF'VODUJPOBMᴷ5BDLMF4PGUXBSF$PNQMFYJUZXJUI%PNBJO%SJWFO %FTJHOBOE'ʯ  • +FSFNZ'BJSCBOL ஶ ϠΪͷ͘͞ΒͪΌΜ

    ຋༁ ʮϓϩάϥϛϯά&MNᴷ҆શͰϝϯςφϯε͠΍͍͢ϑϩϯτΤϯυ ΞϓϦέʔγϣϯ։ൃೖ໳ʯ  • ླ໦ ྅ଠ ʮϓϩΛ໨ࢦ͢ਓͷͨΊͷ5ZQF4DSJQUೖ໳ ᴷ ҆શͳίʔυͷॻ͖ํ͔Βߴ౓ͳܕͷ࢖͍ํ·Ͱʯ  • #PSJT$IFSOZ ஶ ࠓଜ ݠ࢜ ؂म ݪ ོจ ຋༁ ʮϓϩάϥϛϯά5ZQF4DSJQUʕεέʔϧ͢Δ+BWB4DSJQUΞϓϦέʔ γϣϯ։ൃʯ • ௚ੵܕͱ௚࿨ܕʹ͍ͭͯ – ʮू߹ͱͯ͠ͷܕ u"O*OUSPEVDUJPOUP&MNʯ IUUQTHVJEFFMNMBOHKQBQQFOEJYUZQFT@BT@TFUTIUNM – ʮͳͥ࣍ʹֶͿݴޠ͸ؔ਺ܕͰ͋Δ΂͖͔ʯ IUUQTZNPUPOHQPPIBUFOBCMPHDPNFOUSZ