Slide 1

Slide 1 text

5ZQF4DSJQUʹΑΔ (SBQI2-όοΫΤϯυ։ൃ 5ZQF4DSJQUͷܕγεςϜͱσʔλϑϩʔʹண໨ͨ͠એݴతϓϩάϥϛϯά גࣜձࣾ Ұٳ ҏ౻ ௚໵

Slide 2

Slide 2 text

Ϟνϕʔγϣϯ

Slide 3

Slide 3 text

ࡢࠓɺϑϩϯτΤϯυ όοΫΤϯυͷٕज़తؔ৺ࣄʹΪϟοϓ • ΞϓϦέʔγϣϯͷঢ়ଶ؅ཧϞσϧ • σβΠϯγεςϜ • ϓϦϨϯμϦϯά • ŋŋŋ ϑϩϯτΤϯυ όοΫΤϯυ • υϝΠϯϞσϧ • ϨΠϠʔυɾΞʔΩςΫνϟ • $234 • ŋŋŋ ૊৫ͷٕज़࿅౓্͕͕Ε͹্͕Δ΄Ͳɺؔ৺ࣄͷΪϟοϓ͕޿͕͍ͬͯͬͨ

Slide 4

Slide 4 text

3FBDUͰϑϩϯτΤϯυΛ։ൃ͔ͯ͠Βɺ όοΫΤϯυΛॻ͘ͱŋŋŋ • 3FBDUŋŋŋ খ͞ͳؔ਺Λ૊Έ߹Θͤͯએݴతʹॻ͍͍ͯ͘ • όοΫΤϯυ ŋŋŋ ΫϥεΛͨ͘͞Μॻ͍ͯɺϨΠϠʔΛލ͙ͱ %50Ͱͷ஋٧Ίସ͑Λߦͬ ͯɺJOUFSGBDFͰґଘੑͷٯసΛߦͬͯŋŋŋ – ʮŋŋŋϑϩϯτΤϯυͩͱ͜͏͍͏͜ͱɺ͋Μ·Γ΍Βͳ͍ΑͶʯ ։ൃ࣌ͷϝϯλϧϞσϧͷΪϟοϓ͕େ͖͍ ίϯςΩετεΠονͷෛ୲΋େ͖͍

Slide 5

Slide 5 text

όοΫΤϯυ։ൃͷ΍ΓํΛ࠶ߟͯ͠Έ͍ͨ • 3FBDUΛ࢖͍ͬͯΔͱϑϩϯτΤϯυ͸ബ͘ॻ͘͜ͱ͕Ͱ͖Δ • ؔ৺ࣄ͕ҧ͏ͷ͸౰વɻ͔ͱ͍ͬͯɺ΍Γํ͕ҧ͏ͷΛશٙ͘Θͳ͍ͷ΋Ͳ͏ͩΖ͏ ϑϩϯτΤϯυͷঢ়ଶ؅ཧ͸ෳࡶ ͦͷෳࡶͳ΋ͷΛͲ͏ѻ͏͔ɺݱ࣌఺Ͱ࠷ྑͷϞσϧͷͻͱ͕ͭ 3FBDUͷ͸ͣ ʮෳࡶͳঢ়ଶΛͲ͏ѻ͏͔ʯͱ͍͏؍఺ͰɺαʔόʔαΠυ΋ಉ͡Α͏ʹߟ͑ΒΕͳ͍ͷ͔ Α͠ɺ(SBQI2-όοΫΤϯ υ΋ 5ZQF4DSJQUͰॻ͍ͯΈ Α͏

Slide 6

Slide 6 text

ຊ࿦ʹೖΔલʹɺ(SBQI2-όοΫΤϯυͱ $234

Slide 7

Slide 7 text

$234 • ίϚϯυΫΤϦ੹຿෼཭ݪଇ $PNNBOE2VFSZ3FTQPOTJCJMJUZ4FHSFHBUJPO$234 – ΞϓϦέʔγϣϯ࣮૷ͷจ຺Ͱ͸ ࢀরܥ 2VFSZ ͱߋ৽ܥ $PNNBOE ͰҟͳΔϞσϧΛ࢖ ͏Ξϓϩʔν

Slide 8

Slide 8 text

(SBQI2-2VFSZͱυϝΠϯϞσϧͷʮू໿ʯ͸ɺטΈ߹Θ͕ͤѱ͍ • (SBQI2-2VFSZ ˞ .VUBUJPOͰ͸ͳ͍ – ΫϥΠΞϯτ͕ϢʔεέʔεΛߏ੒͢Δ – ू໿୯ҐͰ͸ͳ͘ɺϦιʔε୯ҐͰૢ࡞͞ΕΔ • (SBQI2-2VFSZʹݶΒͣ ू໿Λશ෦Ҿ͘Θ͚ʹ͍͔ͳ͍৔໘ͰͲ͏͢Δ͔ŋŋŋ͸ '"2 – ྫݕࡧҰཡϖʔδ – $234Λద༻ͯ͠ 2VFSZ4FSWJDFΛ࣮૷͢Δͱ͍͏ͷ͕ɺͻͱͭͷղܾࡦ

Slide 9

Slide 9 text

(SBQI2-2VFSZͰ͸ෳࡶͳۀ຿ϩδοΫ͕͋·Γඞཁͳ͍ ˞զʑͷ৔߹ • ܦݧతʹ෼͔͖ͬͯͨ – ଟ͘͸σʔλϕʔεͷ஋Λͦͷ·· (SBQI2-0CKFDUʹ͢Ε͹ྑ͍͚ͩ – ϦονͳʮυϝΠϯϞσϧʯ͸ࢀরܥʹ͸ɺඞཁͳ͍͜ͱ͕ଟ͍ • ͦ΋ͦ΋ %%%ͷू໿͸ࢀরͱ͍͏ΑΓɺू໿શମͷ੔߹ੑΛอͭ͜ͱʹॏ͖͕ஔ͔Ε͍ͯΔŋŋŋ͸ͣ

Slide 10

Slide 10 text

https://speakerdeck.com/qsona/architecture-decision-for-the-next-n-years-at-studysapuri?slide=35

Slide 11

Slide 11 text

1SJTNB

Slide 12

Slide 12 text

1SJTNB • ʮ03.ʯͱ͋Δ͕࣮ࡍ͸ΫΤϦϏϧμʔ ϓϨʔϯͳΦϒδΣΫτΛฦ͢σʔλΞΫηεϥΠϒϥϦ • ࠓ೔ͷ࿩ͱ͸͋·Γؔ܎ͳ͍ ϓϩάϥϛϯάݴޠʹґଘ͠ͳ͍εΩʔϚఆٛɺϚΠάϨʔγϣϯػߏΛ͍࣋ͬͯ Δͷ͕௕ॴɻ։ൃऀମݧ͕ͱͯ΋ྑ͍

Slide 13

Slide 13 text

Domain Model GraphQL Mutation Repository ߋ৽ܥ GraphQL Query Prisma Prisma ࣌ʑ ബ͍ %PNBJO૚ ࢀরܥ (SBQI2-1SJTNB$234

Slide 14

Slide 14 text

Domain Model GraphQL Mutation Repository ߋ৽ܥͷυϝΠϯϞσϧΛͲ͏ॻ͔͘ɻ͔͜͜Β͕ຊ࿦ ߋ৽ܥ GraphQL Query Prisma Prisma 薄い Domain Layer ࢀরܥ

Slide 15

Slide 15 text

8FCΞϓϦέʔγϣϯόοΫΤϯυͷ࣮૷ํ๏Λ࠶ߟ͢Δ

Slide 16

Slide 16 text

ࡢࠓͷϑϩϯτΤϯυͷϓϩάϥϛϯάύϥμΠϜΛߟ͑ͯΈΔ https://zenn.dev/mizchi/articles/oop-think-modern

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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ؔ਺ ΛݺͿɻؔ਺ʹ͸Πϕϯτͷछ ྨʹԠͨ͡Ϟσϧͷঢ়ଶભҠΛ هड़͓ͯ͘͠

Slide 19

Slide 19 text

ঢ়ଶભҠͷؔ਺ ֎քͱ΍Γͱ Γ *0 イベント コマンド

Slide 20

Slide 20 text

model -> model' model -> model' model -> model' ΠϕϯτΛܖػʹঢ়ଶ͕ભҠ͢Δ ŋŋŋ ࣌ܥྻʹج͍ͮͨঢ়ଶ

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

όοΫΤϯυͰ΋ಉ͡Α͏ʹʮ࣌ܥྻʹجͮ͘ঢ়ଶભҠʯͷࢹ఺Ͱߟ͑ΒΕͳ͍͔ • όοΫΤϯυͷੈքͷओͳʮঢ়ଶʯ ŋŋŋ υϝΠϯϞσϧͷঢ়ଶ • υϝΠϯϞσϧͷঢ়ଶΛભҠͤ͞ΔΠϕϯτ ŋŋŋ υϝΠϯΠϕϯτ

Slide 25

Slide 25 text

ͨͱ͑͹ʮ॓ധ༧໿ʯΛྫʹυϝΠϯϞσϧΛվΊͯߟ͑ͯΈΔ • ͲΜͳ؍఺ʹ஫໨ͯ͠ߟ͑ͯΈΔ΂͖͔ – σʔλߏ଄ – &3ਤ – Ϋϥεͷ࣮૷ – ը໘ • ͍ͣΕ΋੩తͳߏ଄ʹয఺Λ౰͍ͯͯΔɻࢹ఺Λม͑ͯΈ͍ͨ – ಈతͳ΋ͷŋŋŋυϝΠϯΠϕϯτ΍ঢ়ଶʹয఺Λ౰ͯͯΈΔͱ

Slide 26

Slide 26 text

ʮ༧໿ʯͷঢ়ଶભҠʹண໨ͯ͠ΈΔ ༧໿׬ྃ Χʔυܾࡁ ࡁΈ Ωϟϯηϧ ॓ധࡁΈ

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

model -> model' model -> model' model -> model' ͓΍

Slide 30

Slide 30 text

྆୺͸֎෦ͱͷΠϯλϑΣʔε model -> model model -> model &WFOU)BOEMFS 8FC"QQͳΒ SPVUFS %#ʹอଘ͠ Ϩεϙϯε 6*

Slide 31

Slide 31 text

model -> model model -> model event &WFOU)BOEMFS 8FC"QQͳΒ SPVUFS %#ʹอଘ͠ Ϩεϙϯε 6* event event ֎ͷੈք ֎ͷੈք ֎ͷੈք Πϕϯτ ˠϞσϧͷঢ়ଶભҠ 🤔 Ͳ͔͜Ͱݟͨͳŋŋŋ

Slide 32

Slide 32 text

ঢ়ଶભҠͷؔ਺ ϥϯλΠϜ΍ϑ ϨʔϜϫʔΫ イベント コマンド ಉ͡

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

ϑϩϯτΤϯυͱόοΫΤϯυͷঢ়ଶ؅ཧ • ϑϩϯτΤϯυͷঢ়ଶ؅ཧ ŋŋŋ ओͳؔ৺ࣄ͸ʮΞϓϦέʔγϣϯͷঢ়ଶʯ • όοΫΤϯυͷঢ়ଶ؅ཧ ŋŋŋ ओͳؔ৺ࣄ͸ʮυϝΠϯϞσϧɺυϝΠϯΦϒδΣΫτͷঢ় ଶʯ ؅ཧ͍ͯ͠Δঢ়ଶͷίϯςΩετ͸ҧ͏΋ͷͷ ঢ়ଶ؅ཧͷϞσϧ͸ࣅͨΑ͏ʹߟ͑ΒΕΔͷͰ͸ͳ͍͔

Slide 37

Slide 37 text

όοΫΤϯυ΋ 5ZQF4DSJQUͰએݴతϓϩάϥϛϯά

Slide 38

Slide 38 text

ʮυϝΠϯΦϒδΣΫτͷঢ়ଶભҠΛએݴతʹهड़ͭͭ͠ *0͔Β෼཭͢Δʯ • ͜ͷίϯηϓτͰ࣮૷ • &MNΞʔΩςΫνϟ΍ॻ੶ %PNBJO.PEFM.BEF'VODUJPOBMΛࢀߟʹ

Slide 39

Slide 39 text

͜ͷελΠϧͰΑ͘࢖͏΋ͷ • type / interface • λά෇͖ϢχΦϯ ௚࿨ܕ • 3FTVMUܕ • ΧϦʔԽ • ܕͷϒϥϯυԽ ίϯύχΦϯΦϒδΣΫτ

Slide 40

Slide 40 text

͋·Γ࢖Θͳ͍΋ͷ • class • ྫ֎ͷ throw – Error Ϋϥε͸࢖͍·͢

Slide 41

Slide 41 text

؆୯ͳϢʔεέʔεྫ

Slide 42

Slide 42 text

5BHΤϯςΟςΟ ू໿ϧʔτ ͷঢ়ଶભҠʹண໨͢Δ 7BMJEBUFE 6OWBMJEBUFE $SFBUFE ೖྗ͕͋ͬͨ ݕূͨ͠ ࡞੒ͨ͠ ˞͜͜Ͱͷʮ$SFBUFEʯ͸͋͘·ͰυϝΠϯΦϒδΣΫτ͕ l࡞੒ࡁΈzʹͳͬͨঢ়ଶͰ͋ͬͯɺσʔλϕʔεʹϨίʔυ Λ௥Ճͨ͠ɺͱ͍͏ঢ়ଶͰ͸ͳ͍

Slide 43

Slide 43 text

5BHΫϥεΛ࡞Δ export class Tag { state: 'Unvalidated' | 'Validated' | 'Created', id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined }

Slide 44

Slide 44 text

export class Tag { state: 'Unvalidated' | 'Validated' | 'Created', id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined } ঢ়ଶભҠલʹ͸֬ఆ͠ͳ͍ϓϩύςΟ͕ VOEFGJOFE ʹͳͬͯ͠·͏ŋŋŋ

Slide 45

Slide 45 text

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 ͦ͜Ͱঢ়ଶ͝ͱʹܕΛఆٛ͢Δ ঢ়ଶ͕ભҠ͢Δ υϝΠϯΠϕ ϯτ͕ൃੜ͢Δ͝ͱʹϞσϧͷ஋ ͕֬ఆ͍ͯ͘͠ͷ͕એݴͰ͖͍ͯ Δ

Slide 46

Slide 46 text

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 ʯ

Slide 47

Slide 47 text

ͪ͜ΒΑΓ΋ŋŋŋ export class Tag { state: 'Unvalidated' | 'Validated' | 'Created', id: TagId | undefined, groupId: RestaurantGroupId, label: string, icon: TagIcon | undefined, sortOrder: number | undefined, builtin: boolean | undefined }

Slide 48

Slide 48 text

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 ͪ͜Βͷํ͕ɺ஋ͷ૊Έ߹Θͤύλʔϯ͕গͳ͘ݫີ

Slide 49

Slide 49 text

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(), } } ঢ়ଶΛભҠͤ͞Δεςοϓ ؔ਺

Slide 50

Slide 50 text

ঢ়ଶΛભҠͤ͞Δεςοϓ ؔ਺ type createTag = (model: ValidatedTag) => CreatedTag const createTag: CreatedTag = (model) => { return { ...model, kind: 'Created', id: generateTagId(), sortOrder: getTagSortOrder({ groupId: model.groupId }), builtin: false, } } ४උ͕੔ͬͯॳΊͯ஋͕֬ఆ ͢ΔͷΛࣗવʹهड़Ͱ͖Δ ͳ͓ getTagSortOrder ͸ *0͕͋Δ ͨΊ %*͢Δɻޙड़

Slide 51

Slide 51 text

Ϟσϧͷܕɺؔ਺ͷܕʹΑͬͯঢ়ଶભҠΛએݴతʹهड़͢Δ 7BMJEBUFE 6OWBMJEBUFE $SFBUFE (model: UnvalidatedTag) => ValidatedTag (model: ValidatedTag) => CreatedTag

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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 } )

Slide 54

Slide 54 text

3FTVMUܕͰঢ়ଶભҠؔ਺Λͭͳ͛ͯɺҰͭͷʮϫʔΫϑϩʔʯΛ࡞Δ 7BMJEBUFE5BH $SFBUFE5BH 6OWBMJEBUFE5BH 7BMJEBUFE5BH 8PSL'MPX

Slide 55

Slide 55 text

type validateTag = (model: UnvalidatedTag) => Result 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ܕΛฦ͢

Slide 56

Slide 56 text

3FTVMUܕͰঢ়ଶભҠؔ਺Λܨ͛ͯɺϫʔΫϑϩʔ υϝΠϯϩδοΫ Λ࡞Δ type WorkFlow = (model: UnvalidatedTag) => Result export const createTagWorkFlow: WorkFlow = (model) => ok(model).andThen(validateTag).andThen(createTag)

Slide 57

Slide 57 text

ϫʔΫϑϩʔͷ࢝·ΓͱऴΘΓ͕ɺ֎քͱͷ JOPVU 7BMJEBUFE5BH $SFBUFE5BH 6OWBMJEBUFE5BH 7BMJEBUFE5BH 8PSL'MPX ೖྗͷ%50 ྫ(SBQI2-*OQVU5ZQF Λ 6OWBMJEBUFE5BHʹม׵ UBH3FQPTJUPSZͰ $SFBUFE5BHΛอଘ

Slide 58

Slide 58 text

(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 } ) }, })

Slide 59

Slide 59 text

ok(model).andThen(workflow).andThen(saveCreatedTag(context)) (SBQI2-*OQVU σʔλϕʔε Pure function Model -> Model *0 JOQVUMPBE *0 PVUQVU

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

σʔλϑϩʔϓϩάϥϛϯά • 3FTVMUܕͰࣦഊͷ͋ΔܭࢉΛ߹੒͠ɺσʔλͷ௨Γಓͱͯ͠ͷܭࢉաఔΛ࡞Δ – ͦ͜ʹσʔλΛ์ΓࠐΉͱɺͦͷதΛ௨ͬͯঢ়ଶભҠͨ͠σʔλ͕ಘΒΕΔ – σʔλΛσʔλͷ··ɺͦͷՄൖੑΛԼ͛ͣʹѻ͍͍ͨɻ݁Ռ class ͷొ৔ػձ͕ͳ͍ • ܭࢉΛҰຊಓʹ͢Δ – େҬ୤ग़͸͠ͳ͍ɻେҬ୤ग़͢Δͱܭࢉ͕ҰຊಓʹͳΒͳ͍ ˠྫ֎Λ࢖Θͳ͍ – ࣦഊͷ෼ذ͸ 3FTVMUͰ߹੒ ˞3FTVMUܕ͸Ϟφυ – ܭࢉ͕ҰຊಓʹͳΔ σʔλ͸ෆมɻೝ஌ෛՙ͕௿͘ͳΔ

Slide 62

Slide 62 text

υϝΠϯϞσϧͷอଘ͸ैདྷ௨Γ 3FQPTJUPSZ export const saveCraetedTag = ({ prisma }: applicationContext) => (model: CreatedTag): ResultAsync => { const { kind: _, ...tag } = model const icon = toIconData(tag.icon) return ResultAsync.fromPromise( prisma.tag.create({ data: { ...tag, ...icon, }, }), PrismaClientError ) } 3FTVMU"TZODGSPN1SPNJTFΛ࢖͏͜ ͱͰ 1SPNJTFΛ 3FTVMUʹแΈɺଞͷ 3FTVMUܕͱ߹੒Ͱ͖Δ ྫ֎΋෧͡ࠐΊΔ͜ͱ͕Ͱ͖Δ

Slide 63

Slide 63 text

3FQPTJUPSZύλʔϯʹ͍ͭͯ • %PNBJO.PEFM.BEF'VODUJPOBMͰ͸ʮ͜ͷख๏ͳΒ 3FQPTJUPSZ͸ඞཁͳ͍ʯͱ͍͏هड़͕͋Δ – ᐌ͘ 3FQPTJUPSZ͸ .VUBCMFͳυϝΠϯϞσϧʹجͮ͘΋ͷ͔ͩΒɺͱͷ͜ͱ – ղઆ͕୹͍͜ͱ΋͋Γจҙ͕Α͘௫Ίͳ͔ͬͨŋŋŋ • ैདྷ௨Γ 3FQPTJUPSZΛར༻ – ͨͩ͠3FQPTJUPSZΫϥεͷΦϒδΣΫτͰ͸ͳ͘ɺؔ਺ – ू໿͸ηΦϦʔ௨Γʹߏங͍ͯ͠Δɻ׶͑ͯݸผͷߋ৽༻ؔ਺ʹ෼ׂ͢Δඞཁ͸ͳ͍ͱߟ͑ͨ

Slide 64

Slide 64 text

XPSLGMPXʹॾʑॻ͍͍ͯ͘ͱɺτϥϯβΫγϣϯεΫϦϓτʹͳΔ • %PNBJO.PEFMJOH.BEF'VODUJPOBM͸τϥϯβΫγϣϯεΫϦϓτ • ŋŋŋ͕ɺڽूੑʹ՝୊͕ग़ͦ͏ͳͷͰɺ߲࣍ͷͱ͓ΓϞδϡʔϧԽͨ͠

Slide 65

Slide 65 text

ΤϯςΟςΟͷܕΛج఺ʹͨ͠ϞδϡʔϧΛ࡞ΓɺίΞυϝΠϯϩδοΫؔ਺͸ͦ͜ʹूΊΔ // customers/objects/tag.ts export interface Tag { id: TagId groupId: RestaurantGroupId label: TagLabel icon: TagIcon sortOrder: number builtin: boolean } export const updateLabel = (label: TagLabel) => (tag: Tag) => ({ ...tag, label }) export const updateIcon = (icon: TagIcon) => (tag: Tag) => ({ ...tag, icon })

Slide 66

Slide 66 text

τϥϯβΫγϣϯεΫϦϓτͰ͸ͳ͘ͳΔ • ίΞυϝΠϯΦϒδΣΫτͷपΓʹɺίΞυϝΠϯϩδοΫؔ਺͕ू·Δ • XPSLGMPX͸ͦͷίΞυϝΠϯϩδοΫؔ਺Λݺͼग़ͯ͠ɺۀ຿ϑϩʔΛ૊ΈཱͯΔ໾ׂʹ – ΦχΦϯΞʔΩςΫνϟͳͲͷ 6TF$BTFͱಉ͡໾ׂ ΞϓϦέʔγϣϯΞʔΩςΫνϟͷશ ମ૾͸ɺैདྷͷΞʔΩςΫνϟͱͦΕ ΄Ͳେ͖͘มΘ͍ͬͯͳ͍

Slide 67

Slide 67 text

έʔεͦͷطଘͷΤϯςΟςΟͷߋ৽ • ͖͞΄Ͳ·Ͱͷ͸ΤϯςΟςΟͷ৽ن࡞੒ • طଘͷΤϯςΟςΟΛߋ৽͢ΔΑ͏ͳϢʔεέʔεͷ৔߹ŋŋŋ – σʔλϕʔε͔ΒΤϯςΟςΟΛ෮ݩ͢Δ – ͦͷΤϯςΟςΟͱ͸ผʹɺೖྗ͕͋Δ

Slide 68

Slide 68 text

7BMJEBUFE *OQVU 6OWBMJEBUFE *OQVU 6QEBUFE 5BH 5BH (SBQI2- %# 3FQPTJUPSZ ͳΜ͔ͩͪ͝Όͭ͘ŋŋŋ

Slide 69

Slide 69 text

(SBQI2- %# 3FQPTJUPSZ *OQVU 5BH 6OWBMJEBUFE $PNNBOE 7BMJEBUFE $PNNBOE ŋŋŋ 6QEBUFE5BH 8PSL'MPX HFU5BH#Z*E ೖྗͱυϝΠϯΦϒδΣΫτΛͻͱͭʹ·ͱΊͨʮίϚϯυʯΛϫʔΫϑϩʔͷೖྗʹ͢Δ ͦͷ্Ͱɺઌ΄Ͳಉ༷ɺ୯ํ޲σʔλ ϑϩʔͷதͰঢ়ଶભҠͤͯ͞໨తͷग़ ྗʹ͚͍ۙͮͯ͘

Slide 70

Slide 70 text

// customers/workflows/updateTag.ts interface UnvalidatedInput { kind: 'Unvalidated' label: string icon?: { symbol: string; type: TagIconType; color?: string | null | undefined } | null | undefined } interface UnvalidatedCommand { input: UnvalidatedInput tag: Tag } interface ValidatedInput { kind: 'Validated' label: TagLabel icon: TagIcon } interface ValidatedCommand { input: ValidatedInput tag: Tag } // Output type UpdatedTag = Tag & { kind: 'Updated' }

Slide 71

Slide 71 text

// customers/workflows/updateTag.ts // substep1: validateCommand type validateCommand = (command: UnvalidatedCommand) => Result // substep2: updateTag type updateTag = (command: ValidatedCommand) => Result // workflow: validateCommand -> updateTag type WorkFlow = (command: UnvalidatedCommand) => Result export const updateTagWorkFlow = (): WorkFlow => (command) => ok(command).andThen(validateCommand).andThen(updateTag)

Slide 72

Slide 72 text

(SBQI2- %# 3FQPTJUPSZ *OQVU 5BH 6OWBMJEBUFE $PNNBOE 7BMJEBUFE $PNNBOE ŋŋŋ 6QEBUFE5BH 8PSL'MPX HFU5BH#Z*E

Slide 73

Slide 73 text

// customers/repos/tagRepository.ts export const findTagById = ({ prisma }: applicationContext) => (id: TagId): ResultAsync => ResultAsync.fromPromise( prisma.tag.findUnique({ where: { id } }), PrismaClientError ).andThen((tag) => (tag ? Tag(tag) : ok(null))) export const getTagById = (context: applicationContext) => (id: TagId): ResultAsync => findTagById(context)(id).andThen((tag) => tag ? ok(tag) : err(new EntityNotFound(`タグがみつかりません: ${id}`)) )

Slide 74

Slide 74 text

// graphql/mutation/updateTag.ts export const updateTagMutation = mutationField('updateTag', { type: UpdateTagPayload, args: { tagId: idArg({ description: 'タグID' }), input: TagInput, }, resolve(_root, { tagId, input }, context) { const workflow = updateTagWorkFlow() const preprocess = TagId(tagId) .asyncAndThen(getTagById(context)) .map((tag) => toUnvalidatedCommand({ input, tag })) const result = preprocess.andThen(workflow).andThen(saveTag(context)) return result.match( ... ) }, })

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

ΑΓෳࡶͳϑϩʔ͸ • جຊతʹ͸ߏ଄͸ಉ͡Ͱɺߏ଄ࣗମ͸ෳࡶʹͳΒͳ͍ • ঢ়ଶભҠͷαϒεςοϓ͕૿͍͑ͯ͘

Slide 77

Slide 77 text

έʔεͦͷυϝΠϯϩδοΫͷ్தͰ *0͕ൃੜ͢Δ৔߹ • ඞͣ͠΋υϝΠϯϩδοΫ࣮ߦલʹಡΈࠐΈΛࡁ·ͤΒΕΔέʔε͹͔ΓͰ͸ͳ͍ • ͨͱ͑͹ŋŋŋ – ࡞੒ͨ͠ΤϯςΟςΟͷฒͼҐஔΛɺσʔλϕʔεʹ໰͍߹Θܾͤͯఆ͢Δ – υϝΠϯϞσϧͷߋ৽ʹɺԿΒ͔֎෦ͷ 8FC "1*Λίʔϧͯ͠औಘͨ͠஋Λར༻͢Δ – ŋŋŋ

Slide 78

Slide 78 text

%FQFOEFODZ*OKFDUJPOΛ࢖͏ • *0ͦͷ΋ͷΛۀ຿ॲཧͷ్த͔ΒऔΓআ͚ͳ͍ͳΒɺ%*Ͱ XPSLGMPX͕ *0 ʹ·ͭΘΔܕ ྫ σʔλ ϕʔε઀ଓΦϒδΣΫτ ʹґଘ͠ͳ͍Α͏ʹ͢Δ – XPSLGMPX͸ *0ʹґଘ͠ͳ͍ؔ਺Ͱ͋Δ͜ͱΛҡ࣋͢Δ • %*͸ΧϦʔԽʹΑΔ෦෼ద༻Ͱ࣮ݱ͢Δ – %PNBJO.PEFM .BEF'VODUJPOBMͰఏҊ͞Ε͍ͯͨख๏

Slide 79

Slide 79 text

// customer/services/tag.ts export type getTagSortOrder = ({ groupId }: { groupId: RestaurantGroupId }) => ResultAsync export const getTagSortOrder = ({ prisma }: applicationContext): getTagSortOrder => ({ groupId }) => ResultAsync.fromPromise( prisma.tag .aggregate({ _max: { sortOrder: true, }, where: { groupId }, }) .then((x) => (x._max.sortOrder ? x._max.sortOrder + 1 : 1)), PrismaClientError ) ΧϦʔԽʹΑΓɺ෦෼ద༻Ͱ͖ΔΑ͏ ʹ͢Δ

Slide 80

Slide 80 text

// graphql/mutation/createTag.ts export const createTagMutation = mutationField('createTag', { type: CreateTagPayload, args: { input: TagInput, }, resolve(_root, { input }, context) { const workflow = createTagWorkFlow(checkTagExists(context), getTagSortOrder(context)) // DI const unvalidatedTag = toUnvalidatedTag({ ...input, groupId: context.operator.groupId, }) const result = workflow(unvalidatedTag).andThen(saveCreatedTag(context)) return result.match(...) }, }) ϫʔΫϑϩʔʹ͸ίϯςΩετ ͕෦෼ద༻͞Εͨؔ਺Λ౉͢

Slide 81

Slide 81 text

// customer/workflows/createTag.ts type createTag = (model: ValidatedTag) => ResultAsync const createTag = (getTagSortOrder: getTagSortOrder): createTag => (model) => { const values = getTagSortOrder({ groupId: model.groupId }) return values.map((n) => ({ ...model, kind: 'Created', id: generateTagId(), sortOrder: n, builtin: false, })) } // workflow: validateTag -> createTag type WorkFlow = (model: UnvalidatedTag) => ResultAsync export const createTagWorkFlow = ( checkTagExists: checkTagExists, // dependency getTagSortOrder: getTagSortOrder // dependency ): WorkFlow => (model) => okAsync(model).andThen(validateTag(checkTagExists)).andThen(createTag(getTagSortOrder)) ϫʔΫϑϩʔ͔Β͸σʔλϕʔείϯ ςΩετ͕ཁΒͳ͍७ਮؔ਺ʹݟ͑Δɻ ͭ·Γɺςετ΍σόοά࣌ɺ७ਮؔ ਺ʹࠩ͠׵͑Δ͜ͱ΋Ͱ͖Δ %*͞Εͨؔ਺Λ஫ೖͭͭ͠΋ɺσʔλ ϑϩʔ͸͜Ε·Ͱ௨Γͷߏ଄ HFU5BH4PSU0SEFS΋ 3FTVMUΛฦ͢ ͷͰɺσʔλϑϩʔͷதͰ݁Ռ͸߹੒ ͞ΕΔ

Slide 82

Slide 82 text

%*Ͱղܾ͢ΔΑ͏ͳ *0Ͱ͸ͳ͍৔߹͸ϫʔΫϑϩʔͷαϯυΠονʹ͢Δͱྑ͍ɺͱͷ͜ͱ https://www.slideshare.net/ScottWlaschin/reinventing-the-transaction-script-ndc-london-2020

Slide 83

Slide 83 text

ߟ࡯

Slide 84

Slide 84 text

ϑϩϯτΤϯυͱͷൺֱ • ࣌ܥྻʹجͮ͘ঢ়ଶભҠ σʔλϑϩʔ Λએݴతʹهड़͢Δɺͱ͍͏ߟ͑ํ͸ಉ͡ʹͳͬͨ – ܕͱখ͞ͳؔ਺ͷએݴతͳهड़ͰɺϑϩʔΛ૊Έ্͛Δ • ҰํɺϫʔΫϑϩʔͷ࣮૷Λ͍ͯ͠Δͱ͖ͷײ֮ʹ͸·ͩڑ཭͕͋Δ – υϝΠϯΠϕϯτͰঢ়ଶભҠɺͱ͍ͬͯ΋ଟ͘ͷ৔߹͸ʮ7BMJEBUFͯ͠ɺೖྗͰυϝΠϯϞσϧ Λߋ৽͢Δʯ͚ͩ • ݁ՌɺϫʔΫϑϩʔ͸ఆܕతͳهड़͕ଟ͘ͳΔ ŋŋŋ ϑϨʔϜϫʔΫԽͰ͖Δ͔΋ • ϑϩϯτΤϯυ͸ͦ͜Λ 3FBDU΍ &MNͳͲͷϑϨʔϜϫʔΫ͕΍͍ͬͯΔ ͔ͩΒɺΠϕϯτʹର͢ΔϞ σϧͷঢ়ଶભҠͱɺͦͷঢ়ଶΛදݱ͢ΔϓϨθϯςʔγϣϯͷهड़ʹूதͰ͖Δ • ΠϕϯτͱΠϕϯτͷͭͳ͗߹Θͤ ྫ3FTVMUܕʹΑΔ߹੒ Λࣗ෼Ͱهड़͍ͯ͠Δͷ͕ݱঢ়

Slide 85

Slide 85 text

ैདྷͷΞʔΩςΫνϟͱͷࠩҟʹ͍ͭͯ • ్தͰ৮Εͨͱ͓Γɺେ࿮ͷΞʔΩςΫνϟ͸͋·ΓมΘ͍ͬͯͳ͍ – 6TF$BTF૬౰ͷ XPSLGMPX – 3FQPTJUPSZ – ίΞυϝΠϯϞσϧ – ू໿ɺΤϯςΟςΟ • ίϯϙʔωϯτͷதͷ࣮૷ͷύϥμΠϜ͕ҟͳΔ – ܕͰۀ຿ͷঢ়ଶɺϑϩʔΛએݴ͢Δ – σʔλϑϩʔϓϩάϥϛϯάʹΑΔɺ୯ํߴσʔλϑϩʔ

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

ݱ࣌఺Ͱͷײ૝ • ࢓্༷ͳ͍ঢ়ଶΛ࡞ΒͣʹࡁΉͨΊɺݎ࿚ • ΑΓෳࡶͳϫʔΫϑϩʔΛ࣮૷ͨ͠৔߹΋ಉ͡ߏ଄ʹऩ·Δɻೝ஌ෛՙ͕௿͍ • 3FTVMUܕʹΑΓܭࢉΛܨ͛ΒΕΔΑ͏هड़͢Δྑ͍ڧ੍ྗ͕ಇ͘ – ͨͩ͠ andThen().andThen().asyncAndThen().map() ͸͕͢͞ʹಡΈͮΒ͍ – 3FTVMU͕ೖΕࢠʹͳͬͯ͘Δͱɺ3FTVMUܕύζϧʹ೰Ή࣌΋ŋŋŋ • )BTLFMMͷ EPه๏ɺ'ͷίϯϐϡςʔγϣϯࣜʹ૬౰͢Δ΋ͷ͕ཉ͍͠ŋŋŋ • ஋ͷ٧Ίସ͑ͷهड़͕ͳ͍ͷ͸ ͱͯ΋ خ͍͠

Slide 88

Slide 88 text

;Γ͔͑Γ https://zenn.dev/mizchi/articles/oop-think-modern

Slide 89

Slide 89 text

·ͱΊ • ࣌ܥྻʹجͮ͘ঢ়ଶભҠͷએݴͱϑϨʔϜϫʔΫଆʹΑΔঢ়ଶભҠɺௐఀ – ϑϩϯτΤϯυ͸ɺͦͷͨΊͷϑϨʔϜϫʔΫͷ࣮૷͕ॆ࣮͍ͯ͠Δ – ؔ਺ܕ͔ΒӨڹΛड͚ͨྑ͍࡞๏ • ʮએݴతϓϩάϥϛϯάʯͷϓϥΫςΟε͸ɺ೥ݱ࣌఺Ͱ͸ܦݧతʹ΋ྑ͍΋ͷ – όοΫΤϯυ։ൃ΋͜ͷߟ͑ํʹऩᏑ͍ͤͯ͘͞ͷ΋ѱ͘ͳ͍ͷͰ͸ ˠ΍ͬͯΈͨΒ޷ײ৮ – ྲྀߦΔ͔Ͳ͏͔͸Θ͔Γ·ͤΜ • ϑϩϯτΤϯυ όοΫΤϯυͷύϥμΠϜΪϟοϓΛগͳ͍͖͍ͯͨ͘͠

Slide 90

Slide 90 text

ัଊ • 5ZQF4DSJQUʹ૊ΈࠐΈͷ 3FTVMUܕ͸ͳ͍ͷͰɺOFWFSUISPXΛ࢖ͬͨɻଞʹ΋ GQUTͳͲͷީิ͕͋Δ • 3FTVMUܕ͸ XPSLGMPXͷߏ੒͚ͩͰͳ͋͘ΒΏΔ৔ॴͰ࢖͏ • 1SPNJTF΋ 3FTVMU"TZODʹΑͬͯ 3FTVMUԽͯ͠߹੒Ͱ͖Δ • UZQFͱ JOUFSGBDFͷ࢖͍෼͚ ŋŋŋ GQUTʹ฿͍ͬͯΔɻಛʹͦ͏͠ͳ͚Ε͹ͳΒ͍ཧ༝͸ͳ͍ͱࢥ͏ • ؔ਺͕σϑΥϧτͰΧϦʔԽ͞Εͳ͍ͷ͸࢓ํ͕ͳ͍ɻ࣌ંΧϦʔԽͨ͠ͷΛ๨Εͯ͸·Δ • SFBEPOMZ͸ԣணͯ͠ɺ࢖ͬͯͳ͍ɻ ͪΌΜͱݕ౼͍ͯ͠ͳ͍

Slide 91

Slide 91 text

͓·͚ ŋŋŋ OFXUZQF׬શίϯετϥΫλ declare const __newtype: unique symbol export type newtype = Type & { readonly [__newtype]: Constructor } export type TagId = newtype<'TagId', string> export function TagId(value: string): Result { return validate(value) ? ok(value as TagId) : err(new ValidationError('IDの形式が不正です')) } υϝΠϯϞσϧͷߏ੒ʹ /PNJOBMͳܕ͕ཉ͍͠ͱ͖͸ɺ͜͏͍ ͏࣮૷Ͱ΍ͬͯ·͢

Slide 92

Slide 92 text

ࢀߟจݙ • 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