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
maku.
January 20, 2018
Programming
2
2.2k
DDD by Functional programming with TypeScript
Gunma.web#30のLTスライドです。
勘違いがあればどうかご指摘ください。
参考
https://qiita.com/nunulk/items/7447e6dadae29a41af3d
maku.
January 20, 2018
Tweet
Share
More Decks by maku.
See All by maku.
Agent on Rails - AIをDDDのレールの上で制御する
childhooooo
0
180
Svelteで作るページビルダー
childhooooo
0
280
JavaScriptを使わない(Phoenix LiveViewの紹介)
childhooooo
0
930
Other Decks in Programming
See All in Programming
AIエンジニアリングのご紹介 / Introduction to AI Engineering
rkaga
8
3.5k
Combinatorial Interview Problems with Backtracking Solutions - From Imperative Procedural Programming to Declarative Functional Programming - Part 2
philipschwarz
PRO
0
120
著者と進める!『AIと個人開発したくなったらまずCursorで要件定義だ!』
yasunacoffee
0
170
Findy AI+の開発、運用におけるMCP活用事例
starfish719
0
1.9k
フルサイクルエンジニアリングをAI Agentで全自動化したい 〜構想と現在地〜
kamina_zzz
0
320
ゲームの物理 剛体編
fadis
0
390
Basic Architectures
denyspoltorak
0
140
ZJIT: The Ruby 4 JIT Compiler / Ruby Release 30th Anniversary Party
k0kubun
1
300
Navigation 3: 적응형 UI를 위한 앱 탐색
fornewid
1
510
JETLS.jl ─ A New Language Server for Julia
abap34
2
470
Implementation Patterns
denyspoltorak
0
140
Rubyで鍛える仕組み化プロヂュース力
muryoimpl
0
230
Featured
See All Featured
Color Theory Basics | Prateek | Gurzu
gurzu
0
160
The Mindset for Success: Future Career Progression
greggifford
PRO
0
200
Ethics towards AI in product and experience design
skipperchong
1
150
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
130
Applied NLP in the Age of Generative AI
inesmontani
PRO
3
2k
So, you think you're a good person
axbom
PRO
0
1.9k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
35
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
210
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
0
26
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
39
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
10
770
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 ʂ ͜ΕงғؾΛઆ໌͢ΔͷͳͷͰɺ·ͬͨͬͯ͘ ਖ਼֬ͳίʔυͰ͋Γ·ͤΜ