2022年10月1日に開催された #postdev での発表です
ϑϩϯτΤϯυͷύϥμΠϜΛࢀߟʹόοΫΤϯυ։ൃΛ࠶ߟ͢Δ5ZQF4DSJQUʹΑΔ (SBQI2-όοΫΤϯυ։ൃגࣜձࣾ Ұٳҏ౻
View Slide
K2VFSZظ.1"XJUIK2VFSZʮ+BWB4DSJQU͕ಘҙʯͳਓ͍͕ͨɺϑϩϯτΤϯυ όοΫΤϯυͱ͍͏ׂߦΘΕͣɺશһ͕ಉ͡ྖҬΛ୲͍ͯͨ͠
7VFKTಋೖظ.1"7VFϑϩϯτΤϯυʮϑϩϯτΤϯυΤϯδχΞʯʮόοΫΤϯυΤϯδχΞʯͱ͍͏ׂ͕গͣͭ͠ͳ͞ΕΔΑ͏ʹ
/VYU࣌/VYU(SBQI2-όοΫΤϯυʮϑϩϯτΤϯυʯʮόοΫΤϯυʯͷׂ͕໌֬ʹϑϩϯτΤϯυͷΈ։ൃΛ͢Δɺͱ͍͏୲ऀ
ํͷٕज़తؔ৺ࣄʹΪϟοϓ• ΞϓϦέʔγϣϯͷঢ়ଶཧϞσϧ• σβΠϯγεςϜ• ϓϦϨϯμϦϯά• ŋŋŋϑϩϯτΤϯυόοΫΤϯυ• υϝΠϯϞσϧ• ϨΠϠʔυɾΞʔΩςΫνϟ• $234• ŋŋŋ৫ͷٕज़࿅্͕͕Ε্͕Δ΄Ͳɺؔ৺ࣄͷΪϟοϓ͕͕͍ͬͯͬͨ
͖͔͚ͬ• ৽نϓϩδΣΫτ্ཱ͕͕ͪΔ• ϑϩϯτΤϯυ 3FBDU3FMBZ3FDPJM Λ࠾༻• (SBQI2-όοΫΤϯυ ŋŋŋ Β͘ 1ZUIPOͰɺΫϥεΛଟ༻ͨ͠ΫϦʔϯΞʔΩςΫνϟతͳઃܭͰ͖͕ͬͯͨɺࠓճͲ͏͢Δ͔
৽نϓϩμΫτখ͞ͳνʔϜͰີߴٞͯ͘͠࡞Γ͍ͨ• ސ٬ͷͲΜͳΛɺͲ͏ղܾ͍͔ͨ͠ ŋŋŋ σΟεΧογϣϯΛେࣄʹ͍ͨ͠– ϑϩϯΤϯυɺόοΫΤϯυؔͳ͘ରυϝΠϯྖҬʹৄ͘͠ͳΓ͍ͨ– ͲΜͳମݧΛɺͲΜͳ 6*ͰɺͲ͏͍͏ϞσϧͰɺͲΜͳσʔλઃܭΛ࣮ͯ͠ݱ͢Δͷ͔ɻॳظϑΣʔζͰશһ͕ͳΔ͓͖͍ͬͯͨ͘͘• ૣظʹׂΛߦ͍͗͢Δͱɺؔ৺ࣄͷஅΛ༠ൃͯ͠͠·͏
3FBDUͰϑϩϯτΤϯυΛ։ൃ͔ͯ͠Βɺ όοΫΤϯυΛॻ͘ͱŋŋŋ• 3FBDUŋŋŋ খ͞ͳؔΛΈ߹Θͤͯએݴతʹॻ͍͍ͯ͘• όοΫΤϯυ ŋŋŋ ΫϥεΛͨ͘͞Μॻ͍ͯɺϨΠϠʔΛލ͙ͱ %50Ͱͷ٧Ίସ͑ΛߦͬͯɺJOUFSGBDFͰґଘੑͷٯసΛߦͬͯŋŋŋ– ʮŋŋŋϑϩϯτΤϯυͩͱ͜͏͍͏͜ͱɺ͋Μ·ΓΒͳ͍ΑͶʯ։ൃ࣌ͷϝϯλϧϞσϧͷΪϟοϓ͕େ͖͍ίϯςΩετεΠονͷෛ୲େ͖͍
όοΫΤϯυ։ൃͷΓํΛ࠶ߟͯ͠Έ͍ͨ• 3FBDUΛ͍ͬͯΔͱϑϩϯτΤϯυബ͘ॻ͘͜ͱ͕Ͱ͖Δ• ؔ৺ࣄ͕ҧ͏ͷવɻ͔ͱ͍ͬͯɺΓํ͕ҧ͏ͷΛશٙ͘Θͳ͍ͷͲ͏ͩΖ͏ϑϩϯτΤϯυͷঢ়ଶཧෳࡶͦͷෳࡶͳͷΛͲ͏ѻ͏͔ɺݱ࣌Ͱ࠷ྑͷϞσϧͷͻͱ͕ͭ 3FBDUͷͣʮෳࡶͳঢ়ଶΛͲ͏ѻ͏͔ʯͱ͍͏؍ͰɺαʔόʔαΠυಉ͡Α͏ʹߟ͑ΒΕͳ͍ͷ͔Α͠ɺ(SBQI2-όοΫΤϯυ 5ZQF4DSJQUͰॻ͍ͯΈΑ͏
վΊͯɺࡢࠓͷϑϩϯτΤϯυͷϓϩάϥϛϯάύϥμΠϜΛߟ͑ͯΈΔhttps://zenn.dev/mizchi/articles/oop-think-modern
&MNΞʔΩςΫνϟhttps://guide.elm-lang.jp/architecture/
update : Msg -> Model -> ( Model, Cmd Msg )update msg model =case msg ofToggleLike ->( { 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 MsgviewLikeButton model =let buttonClass = if model.liked then ...div [ class "like-button" ][ i [ class "fa fa-2x", class buttonClass, onClick ToggleLike ] [] ]&MN7JFX .PEFMΛඳըɻϢʔβʔૢ࡞ʹԠͯ͡ΠϕϯτΛૹΔͱŋŋŋ&MNϥϯλΠϜ͕ VQEBUFؔΛݺͿɻؔʹΠϕϯτͷछྨʹԠͨ͡Ϟσϧͷঢ়ଶભҠΛهड़͓ͯ͘͠
ঢ়ଶભҠͷؔ֎քͱΓͱΓ *0イベントコマンド
model -> model'model -> model' model -> model'ΠϕϯτΛܖػʹঢ়ଶ͕ભҠ͢Δ ŋŋŋ ࣌ܥྻʹج͍ͮͨঢ়ଶ
ঢ়ଶભҠͷؔ ϥϯλΠϜϑϨʔϜϫʔΫイベントコマンドΠϕϯτʹ͍ঢ়ଶΛભҠͤͯ͞ɺ͋ͱϑϨʔϜϫʔΫϥϯλΠϜʹͤΔ
3FEVY"QQMJDBUJPO%BUB'MPXhttps://redux.js.org/tutorials/essentials/part-1-overview-concepts
3FDPJMfunction TextInput() {const [text, setText] = useRecoilState(textState);const onChange = (event) => {setText(event.target.value);};return (Echo: {text});}
3FDPJM• 3FEVYʹΑΔେ͖ͳάϩʔόϧεςʔτɺѻ͍ͮΒ͍ہ໘͋ͬͨ• ΑΓείʔϓΛڱ͘ɺখ͘͞ɺϩʔΧϧεςʔτ VTF4UBUFಉ༷ʹϑοΫͰએݴతʹѻ͍͍ͨ&MNΞʔΩςΫνϟ 3FEVYͰൃݟ͞Εͨྑ͍ϓϥΫςΟε౿ऻͭͭ͠ɺͰ͖ΔݶΓখ͞ͳείʔϓͰঢ়ଶΛѻ͍͚ͬͯΔͱྑͦ͞͏
όοΫΤϯυͰಉ͡Α͏ʹʮ࣌ܥྻʹجͮ͘ঢ়ଶભҠʯͷࢹͰߟ͑ΒΕͳ͍͔• όοΫΤϯυͷੈքͷओͳʮঢ়ଶʯ ŋŋŋ υϝΠϯϞσϧͷঢ়ଶ• υϝΠϯϞσϧͷঢ়ଶΛભҠͤ͞ΔΠϕϯτ ŋŋŋ υϝΠϯΠϕϯτ
ͨͱ͑ʮ॓ധ༧ʯΛྫʹυϝΠϯϞσϧΛվΊͯߟ͑ͯΈΔ• ͲΜͳ؍ʹͯ͠ߟ͑ͯΈΔ͖͔– σʔλߏ– &3ਤ– Ϋϥεͷ࣮– ը໘• ͍ͣΕ੩తͳߏʹযΛ͍ͯͯΔɻࢹΛม͑ͯΈ͍ͨ– ಈతͳͷŋŋŋυϝΠϯΠϕϯτঢ়ଶʹযΛͯͯΈΔͱ
ʮ༧ʯͷঢ়ଶભҠʹணͯ͠ΈΔ༧ྃΧʔυܾࡁࡁΈΩϟϯηϧ॓ധࡁΈ
৽ن༧͕ྃ͢Δલ͔ΒυϝΠϯϞσϧଘࡏ͍ͯ͠Δ༧ྃΩϟϯηϧ॓ധࡁΈೖྗݕূࡁΈೖྗະݕূࡏݿ֬อࡁΈ
ঢ়ଶԿ͔͠ΒͷΠϕϯτΛܖػʹભҠ͢Δ༧ྃΩϟϯηϧ॓ധࡁΈೖྗݕূࡁΈೖྗະݕূࡏݿ֬อࡁΈ༧Λ։࢝ͨ͠ ݕূ͕ྃͨ͠ ࡏݿΛ֬อͨ͠ ༧ΛߦͬͨΩϟϯηϧ͞Εͨ॓ധͨ͠
model -> model'model -> model' model -> model'͓
྆֎෦ͱͷΠϯλϑΣʔεmodel -> modelmodel -> model&WFOU)BOEMFS8FC"QQͳΒSPVUFS%#ʹอଘ͠Ϩεϙϯε6*
model -> modelmodel -> modelevent&WFOU)BOEMFS8FC"QQͳΒSPVUFS%#ʹอଘ͠Ϩεϙϯε6*eventevent֎ͷੈք֎ͷੈք֎ͷੈքΠϕϯτ ˠϞσϧͷঢ়ଶભҠ🤔 Ͳ͔͜Ͱݟͨͳŋŋŋ
ঢ়ଶભҠͷؔ ϥϯλΠϜϑϨʔϜϫʔΫイベントコマンドಉ͡
*0ঢ়ଶભҠ *0Pure functionModel -> Model*0JOQVUMPBE*0PVUQVU
https://www.slideshare.net/ScottWlaschin/reinventing-the-transaction-script-ndc-london-2020
ϑϩϯτΤϯυͱόοΫΤϯυͷঢ়ଶཧ• ϑϩϯτΤϯυͷঢ়ଶཧ ŋŋŋ ओͳؔ৺ࣄʮΞϓϦέʔγϣϯͷঢ়ଶʯ• όοΫΤϯυͷঢ়ଶཧ ŋŋŋ ओͳؔ৺ࣄʮυϝΠϯϞσϧɺυϝΠϯΦϒδΣΫτͷঢ়ଶʯཧ͍ͯ͠Δঢ়ଶͷίϯςΩετҧ͏ͷͷঢ়ଶཧͷϞσϧࣅͨΑ͏ʹߟ͑ΒΕΔͷͰͳ͍͔
ʮυϝΠϯΦϒδΣΫτͷঢ়ଶભҠΛએݴతʹهड़ͭͭ͠ *0͔Β͢Δʯ• ͜ͷίϯηϓτͰ࣮• &MNΞʔΩςΫνϟ͓Αͼ '%%%ຊΛࢀߟʹ
͜ͷελΠϧͰΑ͘͏ͷ• type / interface• λά͖ϢχΦϯ ܕ• 3FTVMUܕ• ΧϦʔԽ• ܕͷϒϥϯυԽ ίϯύχΦϯΦϒδΣΫτ
͋·ΓΘͳ͍ͷ• class• ྫ֎ͷ throw– Error Ϋϥε͍·͢
؆୯ͳϢʔεέʔεྫ
5BHΤϯςΟςΟ ूϧʔτͷঢ়ଶભҠʹண͢Δ7BMJEBUFE6OWBMJEBUFE $SFBUFEೖྗ͕͋ͬͨ ݕূͨ͠ ࡞ͨ͠˞͜͜Ͱͷʮ$SFBUFEʯ͋͘·ͰυϝΠϯΦϒδΣΫτ͕l࡞ࡁΈzʹͳͬͨঢ়ଶͰ͋ͬͯɺσʔλϕʔεʹϨίʔυΛՃͨ͠ɺͱ͍͏ঢ়ଶͰͳ͍
5BHΫϥεΛ࡞Δexport class Tag {state: 'Unvalidated' | 'Validated' | 'Created',id: TagId | undefined,groupId: RestaurantGroupId,label: string,icon: TagIcon | undefined,sortOrder: number | undefined,builtin: boolean | undefined}
export class Tag {state: 'Unvalidated' | 'Validated' | 'Created',id: TagId | undefined,groupId: RestaurantGroupId,label: string,icon: TagIcon | undefined,sortOrder: number | undefined,builtin: boolean | undefined}ঢ়ଶભҠલʹ֬ఆ͠ͳ͍ϓϩύςΟ͕ VOEFGJOFE ʹͳͬͯ͠·͏ŋŋŋ
interface UnvalidatedTag {kind: 'Unvalidated'groupId: stringlabel: stringicon?: { symbol: string; type: TagIconType; color?: string | null | undefined } | null | undefined}interface ValidatedTag {kind: 'Validated'groupId: RestaurantGroupIdlabel: stringicon: TagIcon}export interface CreatedTag {kind: 'Created'id: TagIdgroupId: RestaurantGroupIdlabel: TagLabelicon: TagIconsortOrder: numberbuiltin: boolean}//※この型は実際には出番がないので使っていないexport type Tag = UnvalidatedTag | ValidatedTag | CreatedTagͦ͜Ͱঢ়ଶ͝ͱʹܕΛఆٛ͢Δঢ়ଶ͕ભҠ͢Δ υϝΠϯΠϕϯτ͕ൃੜ͢Δ͝ͱʹϞσϧͷ͕֬ఆ͍ͯ͘͠ͷ͕એݴͰ͖͍ͯΔ
l.BLF*MMFHBM4UBUFT6OSFQSFTFOUBCMFzinterface User {memberId: MemberId | undefinedguestId: GuestId | undefined}interface Member {userId: MemberId}interface Guest {guestId: GuestId}type User = Member | GuestऔΓಘΔͷछྨ֤ଐੑͷੵʹͳΔ ੵYɾ྆ํ VOEFGJOFEɾ྆ํͷ͕ຒ·Δͱ͍͏্༷͋Γಘͳ͍ঢ়ଶ͕ੜ·ΕΔऔΓಘΔछྨ֤ଐੑͷ ্༷͋Γಘͳ͍ঢ়ଶදݱ͠ͳ͍Ϩίʔυʮ͔ͭ "/%ʯ ϢχΦϯʮ·ͨ 03ʯ
ͪ͜ΒΑΓŋŋŋexport class Tag {state: 'Unvalidated' | 'Validated' | 'Created',id: TagId | undefined,groupId: RestaurantGroupId,label: string,icon: TagIcon | undefined,sortOrder: number | undefined,builtin: boolean | undefined}
interface UnvalidatedTag {kind: 'Unvalidated'groupId: stringlabel: stringicon?: { symbol: string; type: TagIconType; color?: string | null | undefined } | null | undefined}interface ValidatedTag {kind: 'Validated'groupId: RestaurantGroupIdlabel: stringicon: TagIcon}export interface CreatedTag {kind: 'Created'id: TagIdgroupId: RestaurantGroupIdlabel: TagLabelicon: TagIconsortOrder: numberbuiltin: boolean}export type Tag = UnvalidatedTag | ValidatedTag | CreatedTagͪ͜Βͷํ͕ɺͷΈ߹Θͤύλʔϯ͕গͳ͘ݫີ
type validateTag = (model: UnvalidatedTag) => ValidatedTagconst validateTag: validateTag = (model) => {// (省略: 値の validation ...)return {...model,kind: 'Validated',groupId: RestaurantGroupId(model.groupId),icon: model.icon ? TagIcon(model.icon) : NoIcon(),}}ঢ়ଶΛભҠͤ͞Δεςοϓ ؔ
ঢ়ଶΛભҠͤ͞Δεςοϓ ؔtype createTag = (model: ValidatedTag) => CreatedTagconst createTag: CreatedTag = (model) => {return {...model,kind: 'Created',id: generateTagId(),sortOrder: getTagSortOrder({ groupId: model.groupId }),builtin: false,}}४උ͕ͬͯॳΊ͕ͯ֬ఆ͢ΔͷΛࣗવʹهड़Ͱ͖Δͳ͓ getTagSortOrder *0͕͋ΔͨΊ %*͍ͯ͠ΔɻࠓճׂѪ
ϞσϧͷܕɺؔͷܕʹΑͬͯঢ়ଶભҠΛએݴతʹهड़͢Δ7BMJEBUFE6OWBMJEBUFE $SFBUFE(model: UnvalidatedTag) => ValidatedTag (model: ValidatedTag) => CreatedTag
ݸผʹఆٛͨ͠ঢ়ଶભҠͷؔΛܨ͍͛ͨ• Ͱɺܭࢉʮ్தͰࣦഊ͢ΔʯՄೳੑ͕͋Δ– ͨͱ͑υϝΠϯϞσϧͷࣄલ݅Λຬͨ͞ͳ͍Τϥʔ– 7BMJEBUJPO&SSPSŋŋŋ– .BY5BH-JNJU&YDFFEFEʜ• ʮ్தͰࣦഊ͢Δʯ͜ͱΛܕͰએݴͰ͖ͳ͍͔ ˠ 3FTVMUܕ
3FTVMUܕͰࣦഊͷՄೳੑͷ͋ΔܭࢉΛҰຊಓʹ߹͢Δimport { Result, ok, err } from 'neverthrow'function itsUnder100(n: number): Result {return n <= 100 ? ok(n) : err(new Error('100より大きい数字です'))}function itsEven(n: number): Result {return n % 2 == 0 ? ok(n) : err(new Error('奇数です'))}function itsPositive(n: number): Result {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 })
3FTVMUܕͰঢ়ଶભҠؔΛͭͳ͛ͯɺҰͭͷʮϫʔΫϑϩʔʯΛ࡞Δ7BMJEBUFE5BH$SFBUFE5BH6OWBMJEBUFE5BH7BMJEBUFE5BH8PSL'MPX
type validateTag = (model: UnvalidatedTag) => Resultconst 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ܕΛฦ͢
3FTVMUܕͰঢ়ଶભҠؔΛܨ͛ͯɺϫʔΫϑϩʔ υϝΠϯϩδοΫΛ࡞Δtype WorkFlow = (model: UnvalidatedTag) => Resultexport const createTagWorkFlow: WorkFlow = (model) =>ok(model).andThen(validateTag).andThen(createTag)
ϫʔΫϑϩʔͷ࢝·ΓͱऴΘΓ͕ɺ֎քͱͷ JOPVU7BMJEBUFE5BH$SFBUFE5BH6OWBMJEBUFE5BH7BMJEBUFE5BH8PSL'MPXೖྗͷ%50ྫ(SBQI2-*OQVU5ZQFΛ6OWBMJEBUFE5BHʹมUBH3FQPTJUPSZͰ$SFBUFE5BHΛอଘ
(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})},})
ok(model).andThen(workflow).andThen(saveCreatedTag(context))(SBQI2-*OQVUσʔλϕʔεPure functionModel -> Model*0JOQVUMPBE*0PVUQVU
ঢ়ଶભҠͷؔ ϥϯλΠϜϑϨʔϜϫʔΫイベントコマンド
σʔλϑϩʔϓϩάϥϛϯά• 3FTVMUܕͰࣦഊͷ͋ΔܭࢉΛ߹͠ɺσʔλͷ௨Γಓͱͯ͠ͷܭࢉաఔΛ࡞Δ– ͦ͜ʹσʔλΛ์ΓࠐΉͱɺͦͷதΛ௨ͬͯঢ়ଶભҠͨ͠σʔλ͕ಘΒΕΔ– σʔλΛσʔλͷ··ɺͦͷՄൖੑΛԼ͛ͣʹѻ͍͍ͨɻ݁Ռ class ͷొػձ͕ͳ͍• ܭࢉΛҰຊಓʹ͢Δ– େҬग़͠ͳ͍ɻେҬग़͢Δͱܭࢉ͕ҰຊಓʹͳΒͳ͍ ˠྫ֎ΛΘͳ͍– ࣦഊͷذ 3FTVMUͰ߹ ˞3FTVMUܕϞφυ– ܭࢉ͕ҰຊಓʹͳΔ σʔλෆมɻೝෛՙ͕͘ͳΔ
ϑϩϯτΤϯυͱͷൺֱ• ࣌ܥྻʹجͮ͘ঢ়ଶભҠ σʔλϑϩʔΛએݴతʹهड़͢Δɺͱ͍͏ߟ͑ํಉ͡ʹͳͬͨ– ܕͱখ͞ͳؔͷએݴతͳهड़ͰɺϑϩʔΛΈ্͛Δ• ҰํɺϫʔΫϑϩʔͷ࣮Λ͍ͯ͠Δͱ͖ͷײ֮ʹ·ͩڑ͕͋Δ– υϝΠϯΠϕϯτͰঢ়ଶભҠɺͱ͍ͬͯଟ͘ͷ߹ʮ7BMJEBUFͯ͠ɺೖྗͰυϝΠϯϞσϧΛߋ৽͢Δʯ͚ͩ• ݁ՌɺϫʔΫϑϩʔఆܕతͳهड़͕ଟ͘ͳΔ ŋŋŋ ϑϨʔϜϫʔΫԽͰ͖Δ͔• ϑϩϯτΤϯυͦ͜Λ 3FBDU &MNͳͲͷϑϨʔϜϫʔΫ͕͍ͬͯΔ ͔ͩΒɺΠϕϯτʹର͢ΔϞσϧͷঢ়ଶભҠͱɺͦͷঢ়ଶΛදݱ͢ΔϓϨθϯςʔγϣϯͷهड़ʹूதͰ͖Δ• ΠϕϯτͱΠϕϯτͷͭͳ͗߹Θͤ ྫ3FTVMUܕʹΑΔ߹ΛࣗͰهड़͍ͯ͠Δͷ͕ݱঢ়
͕࣌ؒແ͍ͷͰɺ࣮ͷৄࡉ·ͨޙ• ͜ͷ··ͰτϥϯβΫγϣϯεΫϦϓτͰɺڽूੑ͕͘ͳͬͯ͠·͏• ΤϯςΟςΟܕͷपลʹɺίΞυϝΠϯϩδοΫͷؔΛ࣮ͯ͠Ϟδϡʔϧׂ͢Δ• ϫʔΫϑϩʔͦͷίΞυϝΠϯϩδοΫΛͬͯϑϩʔΛΈཱͯΔׂ͚ͩʹͳΔ– ΫϦʔϯɾΞʔΩςΫνϟͷ 6TF$BTFͱಉ͡
ैདྷख๏ʹൺֱ͠هड़ྔগͳ͍• σʔλΛσʔλͷ··ӡΜͰ͍ΔͷͰʮ٧Ίସׂ͑ͯͷҟͳΔผछͷΦϒδΣΫτʯʹ͢Δŋŋŋͱ͍͏ඞཁ͕ͳ͍– ͷίϐʔ࣮ࡍʹͱ͜ΖͲ͜Ζ͍ͬͯΔ͕ ͨͩͷσʔλΛׂೖͰهड़Ͱ͖ΔͷͰɺهड़ྔ࠷খ
ݱ࣌Ͱͷײ• ্༷ͳ͍ঢ়ଶΛ࡞ΒͣʹࡁΉͨΊɺݎ࿚• ΑΓෳࡶͳϫʔΫϑϩʔΛ࣮ͨ͠߹ಉ͡ߏʹऩ·Δɻೝෛՙ͕͍• 3FTVMUܕʹΑΓܭࢉΛܨ͛ΒΕΔΑ͏هड़͢Δྑ͍ڧ੍ྗ͕ಇ͘– ͨͩ͠ andThen().andThen().asyncAndThen.map() ͕͢͞ʹಡΈͮΒ͍– )BTLFMMͷ EPه๏ɺ'ͷίϯϐϡςʔγϣϯࣜʹ૬͢Δͷ͕ཉ͍͠ŋŋŋ• ͷ٧Ίସ͑ͷهड़͕ͳ͍ͷ ͱͯخ͍͠
;Γ͔͑Γhttps://zenn.dev/mizchi/articles/oop-think-modern
·ͱΊ• ࣌ܥྻʹجͮ͘ঢ়ଶભҠͷએݴͱϑϨʔϜϫʔΫଆʹΑΔঢ়ଶભҠɺௐఀ– ϑϩϯτΤϯυɺͦͷͨΊͷϑϨʔϜϫʔΫͷ࣮͕ॆ࣮͍ͯ͠Δ• ʮએݴతϓϩάϥϛϯάʯͷϓϥΫςΟεɺݱ࣌Ͱܦݧతʹྑ͍ͷ– όοΫΤϯυ։ൃ͜ͷߟ͑ํʹऩᏑ͍ͤͯ͘͞ͷѱ͘ͳ͍ͷͰ ˠͬͯΈͨΒײ৮– ྲྀߦΔ͔Ͳ͏͔Θ͔Γ·ͤΜ• ϑϩϯτΤϯυ όοΫΤϯυͷύϥμΠϜΪϟοϓΛগͳ͍͖͍ͯͨ͘͠
ัଊ• 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ԣணͯ͠ɺͬͯͳ͍ɻ ͪΌΜͱݕ౼͍ͯ͠ͳ͍
ࢀߟจݙ• 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