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
2.1k
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
250
JavaScriptを使わない(Phoenix LiveViewの紹介)
childhooooo
0
860
Other Decks in Programming
See All in Programming
“あなた” の開発を支援する AI エージェント Bedrock Engineer / introducing-bedrock-engineer
gawa
4
410
2025.01.17_Sansan × DMM.swift
riofujimon
2
570
Асинхронность неизбежна: как мы проектировали сервис уведомлений
lamodatech
0
1.4k
オニオンアーキテクチャを使って、 Unityと.NETでコードを共有する
soi013
0
370
PHPとAPI Platformで作る本格的なWeb APIアプリケーション(入門編) / phpcon 2024 Intro to API Platform
ttskch
0
390
Amazon Nova Reelの可能性
hideg
0
200
カンファレンス動画鑑賞会のススメ / Osaka.swift #1
hironytic
0
180
快速入門可觀測性
blueswen
0
500
DevinとCursorから学ぶAIエージェントメモリーの設計とMoatの考え方
itarutomy
0
150
Внедряем бюджетирование, или Как сделать хорошо?
lamodatech
0
950
rails newと同時に型を書く
aki19035vc
5
710
ドメインイベント増えすぎ問題
h0r15h0
2
570
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
270
27k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
Docker and Python
trallard
43
3.2k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.3k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.2k
Being A Developer After 40
akosma
89
590k
We Have a Design System, Now What?
morganepeng
51
7.3k
Raft: Consensus for Rubyists
vanstee
137
6.7k
Why Our Code Smells
bkeepers
PRO
335
57k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
570
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 ʂ ͜ΕงғؾΛઆ໌͢ΔͷͳͷͰɺ·ͬͨͬͯ͘ ਖ਼֬ͳίʔυͰ͋Γ·ͤΜ