Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DDD by Functional programming with TypeScript
Search
Childhooooo
January 20, 2018
Programming
2
2k
DDD by Functional programming with TypeScript
Gunma.web#30のLTスライドです。
勘違いがあればどうかご指摘ください。
参考
https://qiita.com/nunulk/items/7447e6dadae29a41af3d
Childhooooo
January 20, 2018
Tweet
Share
More Decks by Childhooooo
See All by Childhooooo
Svelteで作るページビルダー
childhooooo
0
230
JavaScriptを使わない(Phoenix LiveViewの紹介)
childhooooo
0
780
Other Decks in Programming
See All in Programming
Cloudflare Workers x AWS Lambdaの組み合わせユースケース / Cloudflare Workers x AWS Lambda Combination Use Case
seike460
PRO
2
310
CSC307 Lecture 09
javiergs
PRO
1
500
ピグパーティにおけるMongoDB CommunityバージョンからAtlasへの移行事例
10969hotaka
0
130
Jetpack for KMP
fornewid
1
290
Modern Angular: Renovation for Your Applications
manfredsteyer
PRO
0
140
企業向け生成AIアプリの 開発から得られた知見
takaakikakei
0
310
今こそ始める、CDKコンストラクトライブラリ開発 ― 入門から実践まで
tmokmss
1
930
実用的かつリーズナブルな 「Azure × Gemini × LINE」~キャラクターBot 実装ライブデモ~
tomodo_ysys
1
170
DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話【デブサミ2024夏】
texmeijin
1
620
リハビリmruby
kishima
1
160
最古の関数型言語「Lisp」ことはじめ / lisp_in_kamiyama
uhooi
1
190
SDCon2024: Enabling DevOps and Team Topologies thru architecture: architecting for fast flow
cer
PRO
0
780
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
36
13k
The MySQL Ecosystem @ GitHub 2015
samlambert
248
12k
10 Git Anti Patterns You Should be Aware of
lemiorhan
652
58k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
23
1.9k
From Idea to $5000 a Month in 5 Months
shpigford
377
46k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
34
1.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
321
23k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
129
32k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
224
21k
Designing Experiences People Love
moore
136
23k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
36
9.1k
Principles of Awesome APIs and How to Build Them.
keavy
124
16k
Transcript
with TypeScript υϝΠϯۦಈઃܭͱؔܕϓϩάϥϛϯά 2017.02.20 Gunma.web#30 Hikaru Otabe
ࣗݾհ w େా෦ߊɹ!DIJMEIPPPPP w લϩέοτؔͷΈࠐΈΤϯδχΞʢ$ ʣ w ࠷ۙ8FCؔͷࣄ w $
1ZUIPO &MJYJS +BWB4DSJQU 1)1FUD Profile
ؔܕϓϩάϥϛϯάͱ͍͑ What comes to your mind ?
ֶ map Ϟφυ ෭࡞༻ ϥϜμࣜ ࢀরಁաੑ ԆධՁ ؔͷ߹ ΧϦʔԽ ΧϦʔԽ
ςετ ͳʹͦΕʁ ฒߦॲཧ ύλʔϯϚον ࠶ؼ
ֶ map Ϟφυ ෭࡞༻ ϥϜμࣜ ࢀরಁաੑ ԆධՁ ؔͷ߹ ΧϦʔԽ ΧϦʔԽ
ςετ ͳʹͦΕʁ ฒߦॲཧ ύλʔϯϚον ࠶ؼ
ࠓճ͢Δಛ lؔܕϓϩάϥϛϯάͰࢀরಁաੑ͕อূ͞ΕΔz
ࠓճ͢Δಛ lؔܕϓϩάϥϛϯάͰࢀরಁաੑ͕อূ͞ΕΔz ͞ΕΔͱ͍͏͔ɺ͢Δɻ
ࢀরಁաੑΛഁյ͢Δͷ Who breaks Referential transparency ?
1. ࠶ೖ 2. ɹมͷࢀরಁաੑΛഁյ ෭࡞༻ͷ͋Δؔ ɹؔͷࢀরಁաੑΛഁյ
࠶ೖʹΑΔࢀরಁաੑͷഁյ > var x = 1 undefined
࠶ೖʹΑΔࢀরಁաੑͷഁյ > var x = 1 undefined > x =
2 2
࠶ೖʹΑΔࢀরಁաੑͷഁյ > var x = 1 undefined > x =
2 2 > x 2
࠶ೖʹΑΔࢀরಁաੑͷഁյ > var x = 1 undefined > x =
2 2 > x 2 Broken!
࠶ೖʹΑΔࢀরಁաੑͷഁյ > var x = 1 undefined > x =
2 2 > x 2 > const y = 1 undefined > y = 2 TypeError Broken!
ؔͷ෭࡞༻ʹΑΔࢀরಁաੑͷഁյ > var counter = 0 undefined > const count
= (x) => counter += x undefined > count(3) 3 > count(3) 6
ؔͷ෭࡞༻ʹΑΔࢀরಁաੑͷഁյ > var counter = 0 undefined > const count
= (x) => counter += x undefined > count(3) 3 > count(3) 6 Broken!
υϝΠϯۦಈઃܭ Domain-Driven Design
υϝΠϯϞσϧͷߏཁૉ w ΦϒδΣΫτ w ΤϯςΟςΟ w αʔϏε w FUDʜ
υϝΠϯϞσϧͷߏཁૉ w ΦϒδΣΫτ w ΤϯςΟςΟ w αʔϏε w FUDʜ
υϝΠϯϞσϧͷߏཁૉ w ΦϒδΣΫτ w ΤϯςΟςΟ w αʔϏε w FUDʜ ؔܕϓϩάϥϛϯάͰͲͷΑ͏ʹදݱ͢Δ͔ʁ
υϝΠϯϞσϧͷߏཁૉ w ΦϒδΣΫτ w ΤϯςΟςΟ w αʔϏε w FUDʜ ؔܕϓϩάϥϛϯάͰͲͷΑ͏ʹදݱ͢Δ͔ʁ
ʂ ᘳͳ࣮ફ͍͠Ͱ͢
ۜߦޱ࠲ΞϓϦέʔγϣϯͷྫ
ΤϯςΟςΟʢؔܕϓϩάϥϛϯάʣ class Account { constructor(public id: UserID, public balance: Balance)
{ Object.freeze(this); } debit(amount: number):Balance { if(this.balance.amount < amount) { return [“error”, “This is an error message.”]; } else { return [“success”, ɹnew Account(this.id, new Balance(this.balance.amount - amount))]; } } credit(amount: number):Balance { return [“success”, new Account(this.id, new Balance(this.balance.amount + amount))]; } }
จࣈ͕খ͍͞
GitHub github.com/childhooooo/ddd-fp-ts
ΦϒδΣΫτ w ߴΛද࣌͢ɺ/VNCFSܕͰͳ͘ɺ#BMBODFܕΛ w ෆมੑʜҰੜͨ͠ΒɺଐੑΛมߋ͢Δ͜ͱग़དྷͳ͍ w ՁੑʜࢀরઌͰͳ͘ɺଐੑͰஅ Value objects
ΦϒδΣΫτ class Balance { constructor(public amount: number = 0) {
Object.freeze(this); } } const balance = new Balance(100); balance.amount = 50 //error
ΤϯςΟςΟ w ಉҰੑʜଐੑ͕ҟͳ͍ͬͯͯɺ*%͕ಉ͡ͳΒಉҰͷ ͷͱͯ͠ѻ͏ w ϥΠϑαΠΫϧͷ࿈ଓੑ Entities
ΤϯςΟςΟʢҰൠతͳΦϒδΣΫτࢦʣ class Account { constructor(public id: UserID, public balance: Balance)
{} debit(amount: number):string { if(this.balance.amount < amount) { return “error”; } else { this.balance = new Balance(this.balance.amount - amount); return “success”; } } credit(amount: number):string { this.balance = new Balance(this.balance.amount + amount); return “success”; } }
ΤϯςΟςΟΛ͏ʢҰൠతͳΦϒδΣΫτࢦʣ let account = new Account(“A”, new Balance(200)); console.log(account.balance.amount); //200
result = account.debit(100); console.log(result, account.balance.amount); //“success” 100
ΤϯςΟςΟΛ͏ʢҰൠతͳΦϒδΣΫτࢦʣ let account = new Account(“A”, new Balance(200)); console.log(account.balance.amount); //200
result = account.debit(100); console.log(result, account.balance.amount); //“success” 100 Broken!
ΤϯςΟςΟʢؔܕϓϩάϥϛϯάʣ class Account { constructor(public id: UserID, public balance: Balance)
{ Object.freeze(this); } debit(amount: number):Account { if(this.balance.amount < amount) { return [“error”, “This is an error message.”]; } else { return [“success”, ɹnew Account(this.id, new Balance(this.balance.amount - amount))]; } } credit(amount: number):Account { return [“success”, new Account(this.id, new Balance(this.balance.amount + amount))]; } }
ΤϯςΟςΟΛ͏ʢؔܕϓϩάϥϛϯάʣ const pattern = require(“matches”).pattern; //matches.js const account = new
Account(new UserID(1), new Balance(200)); const operated = account.debit(100); pattern({ ‘[“success”, a@Account ]’: (a) => console.log(a.balance.amount), ‘[“error”, message ]’: (message) => console.log(message) })(operated); //100
αʔϏε w ΦϒδΣΫτΤϯςΟςΟʹଐ͞ͳ͍ॏཁͳॲཧ w ಠཱͨ͠ΠϯλʔϑΣΠε w ঢ়ଶΛ࣋ͨͳ͍ Services
αʔϏε const transfer = (from: Account, to: Account, amount: number)
=> { const [res, debited] = from.debit(amount); return if_success([res, debited, …to.credit(amount)], ([a, credited]) => [“success”, a, credited[1]]); }
αʔϏεΛ͏ const pattern = require(“matches”).pattern; //matches.js const from = new
Account(new UserID(1), new Balance(500)); const to = new Account(new UserID(2), new Balance(300)); const transferred = transfer(from, to, 300); pattern({ ‘[“success”, f, _]’: (f) => console.log(f.balance.amount), ‘[“error”, message]’: (message) => console.log(message) })(transferred); //200
ঢ়ଶΛ͏ܭࢉʹ͍ͭͯ State monad
ҰൠతͳΦϒδΣΫτࢦͷ߹ > const account = new Account(new UserID(5)) undefined //ҎԼɺߴΛฦؔ͢showBalance(account)͕ఆٛͯ͋͠Δͱ͢Δɻ
> showBalance(account) 0 > account.credit(200) undefined > account.debit(100) undefined > account.debit(50) undefined > showBalance(account) 50
ҰൠతͳΦϒδΣΫτࢦͷ߹ > const account = new Account(new UserID(5)) undefined //ҎԼɺߴΛฦؔ͢showBalance(account)͕ఆٛͯ͋͠Δͱ͢Δɻ
> showBalance(account) 0 > account.credit(200) undefined > account.debit(100) undefined > account.debit(50) undefined > showBalance(account) 50 Broken!
ؔܕϓϩάϥϛϯάঢ়ଶΛ࣋ͨͳ͍ͣɾɾɾ
ؔܕͰී௨ʹͬͯΈΔ > const a0 = new Account(new UserID(5)) undefined >
showBalance(a0) 0 > const a1 = a0.credit(200) undefined > const a2 = a1.debit(100) undefined > const a3 = a2.debit(50) undefined > showBalance(a3) 50
ؔܕͰී௨ʹͬͯΈΔ > const a0 = new Account(new UserID(5)) undefined >
showBalance(a0) 0 > const a1 = a0.credit(200) undefined > const a2 = a1.debit(100) undefined > const a3 = a2.debit(50) undefined > showBalance(a3) 50 ຊʹ͜ΕͰ͍͍ͷʁ
ؔܕͰී௨ʹͬͯΈΔ > const a0 = new Account(new UserID(5)) undefined >
showBalance(a0) 0 > const a1 = a0.credit(200) undefined > const a2 = a1.debit(100) undefined > const a3 = a2.debit(50) undefined > showBalance(a3) 50 ݁ہɺBɾBͱ͍ͬͨঢ়ଶΛ͍࣋ͬͯΔ
ͦΕͳΒͬͪ͜ͷ΄͏͕Θ͔Γ͍͢ > const account = new Account(new UserID(5)) undefined >
showBalance(account) 0 > account.credit(200) undefined > account.debit(100) undefined > account.debit(50) undefined > showBalance(account) 50
ͦΕͳΒͬͪ͜ͷ΄͏͕Θ͔Γ͍͢ > const account = new Account(new UserID(5)) undefined >
showBalance(account) 0 > account.credit(200) undefined > account.debit(100) undefined > account.debit(50) undefined > showBalance(account)() 50 ͰɺͲ͏ͬͯࢀরՁੑΛอͭͷ͔
಄ͷྑ͍ਓߟ͑ͨ
͜ΜͳͷͲ͏͔ > const account = new Account(new UserID(5)) undefined >
showBalance(account, “Կ͍ͯ͠ͳ͍”) 0 > showBalance(account, “200आΓͯɺ100ିͯ͠ɺ50ିͨ͠”) 50
͜ΜͳͷͲ͏͔ > const account = new Account(new UserID(5)) undefined >
showBalance(account, “Կ͍ͯ͠ͳ͍”) 0 > showBalance(account, “200आΓͯɺ100ିͯ͠ɺ50ିͨ͠”) 50 ঢ়ଶΛ໌ࣔతʹ͢
͜ΜͳͷͲ͏͔ > const account = new Account(new UserID(5)) undefined >
showBalance(account, “Կ͍ͯ͠ͳ͍”) 0 > showBalance(account, “200आΓͯɺ100ିͯ͠ɺ50ିͨ͠”) 50 Ҿ͕ҧ͏ͷͰɺࢀরՁੑ͕อͨΕͨʂ ঢ়ଶΛ໌ࣔతʹ͢
ঢ়ଶΛ͏ܭࢉʹ͍ͭͯ State monad
ঢ়ଶΛ͏ܭࢉʹ͍ͭͯ State monad
4UBUFϞφυ > const account = new Account(new UserID(5)) undefined >
const state = new State(“200आΓͯɺ100ିͯ͠ɺ50ିͨ͠”) undefined > showBalance(state.runState(account)) 50 ʂ ͜ΕงғؾΛઆ໌͢ΔͷͳͷͰɺ·ͬͨͬͯ͘ ਖ਼֬ͳίʔυͰ͋Γ·ͤΜ