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
Goでやや大きいプロダクトのアーキテクチャを検討したり実装したりしたおはなし
Search
yami20
July 17, 2020
Technology
820
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Goでやや大きいプロダクトのアーキテクチャを検討したり実装したりしたおはなし
yami20
July 17, 2020
Other Decks in Technology
See All in Technology
AI Agent を本番環境へ―― Microsoft Foundry × Azure Serverless で作る Enterprise-Ready な基盤
shibayan
PRO
1
1k
[MIRU26] To What Extent Does MLLM-as-a-Judge Exhibit Cross-Model Preference Bias?
keio_smilab
PRO
0
100
AI工学特論: MLOps・継続的評価
asei
11
3.1k
MIRU 2026 チュートリアル
keisuke198619
0
450
AI駆動開発は個人技からチーム戦へ:組織でAIを使いこなすための実践設計
moongift
PRO
0
390
BigQuery を検索ソースとした AI Agent の作り方って 〇〇 通りあんねん
satohjohn
0
150
AI驚き屋発見器
yama3133
2
400
Master Dataグループ紹介資料
sansan33
PRO
1
4.8k
オートマトンと字句解析でRoslynを読む
tomokusaba
0
130
Claude Code並行開発環境の ムダ‧ムラ‧ムリを見直した話
muranakaaa
2
390
CloudWatchから始めるAWS監視
butadora
0
310
MCPをつなげて作る組織横断のAIエージェント基盤
tsubakimoto_s
0
530
Featured
See All Featured
Context Engineering - Making Every Token Count
addyosmani
9
1k
Designing for Timeless Needs
cassininazir
1
430
Designing Experiences People Love
moore
143
24k
Accessibility Awareness
sabderemane
1
170
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
190
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Chasing Engaging Ingredients in Design
codingconduct
0
250
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Optimising Largest Contentful Paint
csswizardry
37
3.9k
Between Models and Reality
mayunak
4
380
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
240
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Transcript
GoͰେ͖͍ϓϩμΫτͷ ΞʔΩςΫνϟΛݕ౼ͨ͠Γ࣮ͨ͠Γͨ͠ ͓ͳ͠ Remote.go#1 2020.07.17 @yami20
ͩΕʁ • @yami20 (ΠϯλʔωοτҾ͖͜ΓͳͷͰಛʹใग़ͯ͜ͳ͍Ͱ͢) • Engineer@Finatext • อݥࣄۀͰαʔόʔαΠυ։ൃ • Go1.6?1.7?͋ͨΓͰͪΐͬͱ͞ΘͬͯΕ͍ͯͨ
• ڈͷΕ͔Β·ͨ৮Γ࢝Ίͨͱ͜Ζ • ͜ͷձͷλΠτϧʹʮ্ڃऀ͚ʯͱ͔ॻ͔ΕͯͯŜƄŞŲƄſͯ͠·͢
͖ΐ͏͓ͳ͢͠Δ͜ͱ • ͦͦ͜͜େ͖͍ɾෳࡶͳϓϩμΫτΛGoͰͭ͘Γ·ͨ͠ɻ • ࣮ΞʔΩςΫνϟͷબఆtipsΛɺͦͷҙਤΛ৫Γަͥͳ͕Β͓ ͯ͠ΈΑ͏ͱࢥ͍·͢ɻ
ͦͷ͜͜Ζʁ • ʮ͖Ε͍ͳͷ͜͏͍͏࣮ͩʯͱ͍͏ใͨ͘͞Μ͋Δɻ • ͖Ε͍ͳ࣮Λ͍ٻΊଓ͚Δ͜ͱ͕࣮ʹ͓͍ͯ࠷దղͰͳ͍ɻ • ʮ͋Δ࣮ʹΑͬͯͲͷ༷ͳ՝͕ղܾ͞ΕΔ͔ʯͱ͍͏༗ػతͳͭͳ͕ Γɺԡ͑͞Δ͖ϙΠϯτ/ࣺͯͯΑ͍ϙΠϯτΛڞ༗͢Δ͜ͱͰɺΑΓ Α͍࣮Λࢦ͢ਓʑͷࢀߟʹͳΕ͍͍ͳ͊ɻ
͓ͳ͠͠ͳ͍͜ͱ ͘͢͝৽͍͠ใɺΠέΠέͳ࣮ग़͖ͯ·ͤΜɻ ޠΓਚ͘͞ΕͨثͨͪΛखʹɺଥͳ࣮Λࢦ͓͢ͳ͠Ͱ͢ɻ
ຊ
ཁ݅ɾنɾಛੑ ʙอݥϓϥοτϑΥʔϜγεςϜʙ •(͜ͷลࢀߟఔʹॻ͍͚ͨͩͳͷͰඈ͠·͢) • ཁ݅ • APIαʔόʔ(SPAͷbackend) • ͪͳΈʹରʹͳΔfrontendͷͳ͠ https://speakerdeck.com/slont/bokufalsekangaetasaikiyoufalsevueakitekutiya
• ෳͷอݥձ͕ࣾΛઃܭɾొ͠ɺސ٬ͷਃࠐΛड͚͚ͨΓɺܖ৹ࠪͨ͠Γཧͨ͠Γɺͦͷଞਵ͢Δػೳ͕ʹΐʹΐɻ • ن • ΞυςΫήʔϜʹൺΔͱτϥϑΟοΫ֨ஈʹ͓ͱͳ͍͠ɻ1ਓ͕1ʹԿेճՃೖͨ͠Γ͠ͳ͍͠ɺ࿈ଧͯ͢͠ఢ͍ͳ͍ɻ • PFͰ͋Δ͜ͱΛ౿·͑ͯҰൠతͳECͱ͔ͷنײΛΠϝʔδ͍͚ͯͨͩ͠Εɻ • ಛੑ • σʔλͷϥΠϑαΠΫϧ͕͍ɻ୯Ґ͕جຊɺਓੜ୯ҐͷऔҾ͋Δ • σʔλͷߏ͕ෳࡶ. ଐੑϦϨʔγϣϯͱʹ͔͘ଟ͍. • γεςϜͷण໋͕͍ɻσʔλͷϥΠϑαΠΫϧ͕͍ͷ͋Δ͠ɺPFͳͷͰ͓͍ͦΕͱด͡ΒΕͳ͍ͱ͍͏ͷ͋Δɻ • PFͰ͋ΔͨΊɺॳظʹग़Δཁ͕݅ͯ͢ͱݶΒͳ͍ • ࢥ͍ΑΒͳ͍֦ு͕ඞཁʹͳΔՄೳੑ͕͋Δ
࠾༻ͨ͠ߏ • ϞϊϦε • શʹ୯ҰγεςϜͱ͍͏Θ͚Ͱͳ͍Ͱ͕͢ɻ • ໌Β͔ʹੑ࣭ͷҟͳΔ෦͕ͨ͠ɺAPIेຊͷίϯϙʔωϯτ3ͭͱ͍͏ঢ়ଶɻ • ϨΠϠʔυΞʔΩςΫνϟ(෩) •
ݫີͳͦΕͰͳ͍ • DIP(ґଘੑͷٯస)ΈࠐΉ
ϞϊϦεͱ͍͏બ
ϞϊϦεͱϚΠΫϩαʔϏε • ϞϊϦεʹΛࡴ͞Εͨܦݧ͋Δɻ • ϑϩϯτ͔Βཧը໘·Ͱ͕ͯ͢ಉࠝ͞Εɺ100ਓҎ্ͷ։ൃऀ͕ҰͭͷϦϙδτϦʹ commit͠ʮdeployฒͼ·͢ʯͱΛ͔͚߹͍ɺσϓϩΠࣦഊ͢Δͱଞ෦ॺʹౖΒΕΔσΟ ετϐΞ • ϚΠΫϩαʔϏεʹଜΛম͔Εͨܦݧ͋Δɻ •
1~3ςʔϒϧ૬ͷσʔλຖʹઐ༻ͷAPI͕ཚཱͯ͠૬ޓʹԿࢀর͍͋͠ɺ͍ͭͲ ͜ͰԿͷॲཧ͕ى͖͍ͯΔ͔Θ͔Βͳ͍σΟετϐΞ • ݁ہɺ͓࡞๏ʹࠩͷ͋ΔଟͷγεςϜΛͯ͢Ѳ͠ͳ͚ΕԿΘ͔Βͳ͍ɻ
ϞϊϦεͱϚΠΫϩαʔϏε • ϞϊϦε • ϝϯςෆՄೳͳ΄Ͳίʔυɾؔऀ͕ଟ͘ͳΔͱͭΒ͍ • ಉҰϦϙδτϦ͔ͩΒͱແࠩผʹࢀরΛΏΔ͢ͱΘ͚Θ͔ΒΜ͘ͳΔ • ϚΠΫϩαʔϏε •
ෆదͳׂʹΑͬͯຊདྷඞཁͷͳ͍ॲཧ͕૿Ճ͢ΔͱͱͯͭΒ͍ • Ұൠతʹ࣮ྔ૿͑Δ͠ɺΠϯϑϥߏ໘Ͱͷݕ౼ࣄ߲ɾઃఆ૿͑Δ
ϞϊϦεͱ͍͏બ • ࠷దͳϚΠΫϩαʔϏεͷཻΛࣄલʹݟग़͢͜ͱඇৗʹқ͕ߴ͍ɻ • ༷௨Γʹ࣮͍ͯͨ͠ͷʹʮ࣮Aͷσʔλ͚ͩͩͱΓͳͯ͘ɺৗʹBͱCҰॹʹཁΔΜͩ...ʯͱ͔ݴΘΕ͕ͪ • Goͬͯهड़ྔࣗମগͳ͘ͳ͍Ͱ͢ΑͶ? (ڞײΛٻΊΔѹྗ) • γϯϓϧͰಡΈॻ͖͍͢͠ɺʮָΛͤͯ͘͞ΕΔʯΈ߇͑Ίɻ
• ϚΠΫϩαʔϏεͰͷهड़૿Ճͱ߹ΘͤΔͱͪΐͬͱϔϏʔɻ • αʔϏεͷ্ཱͪ͛࣌ͰϞϊϦε๊͕͑Δ͕ੜ͡ΔϦεΫখ͍͞ • ʮϞϊϦεͷ··ʯҭͯͳͯ͘͢ΉΑ͏ʹదͳઃܭɾ੍Λ͠ɺ͕࣌དྷͨΒׂ͢ΕΑ͔Ζ͏ͳ ͷͩ • αʔϏεΛҭ͍ͯͯ͘தͰదͳׂཻ͕ݟ͑ͯ͘Δͱ͍͏ϝϦοτ͋Δɻ
ͱ͜ΖͰ • ϞϊϦε(স)ͬͯݴΘΕΔ͘Β͍ͷ֮ޛͰ࣮͍ͯͨ͠ΜͰ͕͢ɺ ͘͜͜Β͍?ͰϞδϡϥϞϊϦεͱ͍͏֓೦͕ఏএ͞Ε͍ͯͨͷ Ͱ͢Ͷɻ • ͦͷͱ͓Γʹ͍ͬͯΔͱ·Ͱݴ͑·ͤΜ͕ɺΞϓϩʔνͱͯ͠ Θ͔ΓΈ͕;͔͍
ϨΠϠʔυΞʔΩςΫνϟ(෩)ͱ͍͏બ
ϨΠϠʔυΞʔΩςΫνϟ(෩) • ࣮ΞʔΩςΫνϟʹۜͷؙͳ͘ɺνʔϜͱϓϩδΣΫτͷঢ়گ ʹΑͬͯ࠷దղมΘΔɻ • ֎෦ଓ͕ͳ͘ɺ͔ͭ20APIʹຬͨͳ͍Α͏ͳγεςϜΛগਓͰ࡞ ΔͷͰ͋ΕMVCͰશવྑ͍ͱࢥ͏ɻ • αʔϏε͕େنͰෳࡶɺ͔ͭ৫ͷख़͕ߴ͚ΕΫϦʔϯΞʔ ΩςΫνϟͰ࡞ΓࠐΈͨ͘ͳΔͱࢥ͏ɻ
ϨΠϠʔυΞʔΩςΫνϟ(෩) • લఏ • ߏΛϞϊϦεͱ͢Δ͜ͱʹਵͯ͠ɺதظతʹʮϢʔεέʔεʹ Ԡͯ͡αʔϏεΛ͍ͨ͠ʯͱߟ͍͑ͯΔɻ • Ϣʔεέʔε͕໌֬ʹͳΔΑ͏ͳ࣮ΞʔΩςΫνϟ/੍Λઃ͚͍ͨ • อݥͱ͍͏౷తͳϏδωεʹج͍ͮͯ࡞ΔαʔϏεͰ͋ΓɺϏδωε
ϩδοΫ͕ෳࡶɾॏཁͱͳΓಘΔɻ => DDDͱੑͷߴ͍ΞʔΩςΫνϟ͕ྑ͍ͱߟ͑ͨɻ
ϨΠϠʔυΞʔΩςΫνϟ(෩) • ͳͥΫϦʔϯΞʔΩςΫνϟ(ҎԼCA)Λબͳ͔ͬͨͷ͔? • CA͔ͨ͠ʹΑ͘Ͱ͖͍ͯΔͱ͓͏ • ͔͠͠ͳ͕ΒɺΞʔΩςΫνϟࣗମͷֶशίετͷߴ͕͞൱Ίͳ͍ • ͚͕ݫີɾৄࡉͰ͋Δ͕Ώ͑ʹɺʮ͏·͘Θ͚ͳ͚Ε͍͚ͳ͍ʯڧ੍͍Λײ͡Δ ʢݸਓͷײͰ͢)
• ݫີʹΘ͚ΒΕΔ͜ͱڧΈ͕ͩɺڧΈΛڗड͢Δʹ࠷ॳ͔Βߴ͍ݟ͕ٻΊΒΕΔɻ গਓɾ্ཱͪ͛ஈ֊ͷϓϩδΣΫτʹ͓͍ͯͷίεύ͍͔΄Ͳ͔?
ϨΠϠʔυΞʔΩςΫνϟ(෩)ͱ͍͏બ • DDD͖ͷΞʔΩςΫνϟͷதͰൺֱతૉ(ݸਓͷײͰ͢)ͳϨΠϠʔυ ΞʔΩςΫνϟͰυϝΠϯΛ͖Ε͍ʹอͪɺϢʔεέʔεΛΘ͔Γ͘͢දݱ ͢Δ͜ͱͰ͖Δɻ • ७ਮͳϨΠϠʔυΞʔΩςΫνϟinfraͷґଘΛ࣋ͭͨΊςετͮ͠Β͍ • DIPΛՃ͑ΕΓ͍ͨ͜ͱेʹͰ͖Δɺͣɻ
ґଘੑͷٯస(DIP) ```go:application/policy.go package application type Policy struct { // DIP.
infraʹґଘͤͣ͞ʹdomainͷinterfaceʹґଘͤ͞Δ policyRepo domain.PolicyRepository } func (p *Policy) Get(id string) (*model.Policy, error) { // ... res, err := p.policyRepo.Get(id) if err != nil { return nil, err } return res, nil } ``` ```go:domain/repository.go package domain // application͔Βݟ͑ΔॴʹinterfaceΛஔ͘ type PolicyRepository interface { Get(id string) (*model.Policy, error) } ```
ϨΠϠʔυΞʔΩςΫνϟ෩ʹຬͨ͢ͱ... ```go:infra/db/policy.go package infra // infra/db ʹ interface Λຬͨ͢۩ମతͳ࣮Λஔ͘ type
PolicyRepository struct { sess *dbr.Session } func (p *PolicyRepository) Get(id string) (*model.Policy, error) { // SQLͱ͔ // p.sess. ... } ```
ΫϦʔϯΞʔΩςΫνϟ෩ʹຬͨ͢ͱ... ```go:infra/db/policy.go // infra type DBSess struct { sess *dbr.Session
} func NewDBSess() *DBSess { // DBଓ } func (d *DBSess) Query(stmt string, args ...interface{}) () {} ``` ```go:interfaces/db.go // interfaces type DBSess interface { Query(stmt string, args ...interface{}) (Rows, error) } type Rows interface { Scan(...interface{}) error Next() bool Close() error } type PolicyRepository struct { sess DBSess } func (p *PolicyRepository) Get(id string) (*model.Policy, error) { // SQLͱ͔ // p.handler.... } ```
CAͪΐͬͱΉ͔͍ͣ͠ ཆ৩ͷ՝/ٖࣅίʔυͰൺֱ͢ΔͷͪͱͣΔ͍Ͱ͕͢...ͱ͖ͬͭʹ ͍͘͜ͱ൱Ίͳ͍...ɻ ͖Ε͍ͳநԽͰ͋Δ͜ͱΘ͔Δɻ ͔͠͠interfacesͱinfra͕ผΕ͍ͯΔԸܙͬͯԿ͚ͩͬ? ͦΕʹΑͬͯͤʹͳΔہ໘ࣗͨͪʹͲΕ͚ͩ๚ΕΔΜ͚ͩͬ?
݁ہ࡞ͬͨߏ (αϯϓϧ. ΈͮΒ͍) ϨΠϠʔυΞʔΩςΫνϟDIPͷ ใੈͷதʹͨ͘͞Μ͋Δ͠৽ ͍͜͠ͱΛ͍ͯ͠ΔΘ͚Ͱͳ͍Ͱ ͕͢...
ઃܭͷझࢫ • ࣮ફDDDʹ͍͏ʮ؇͔ͳϨΠϠʔԽΞʔΩςΫνϟʯ͕ϕʔε • ʮ؇͔ʯͱɺԼͷ͚ͩͰͳ͘Լํͷ͍ͣΕͬͯΑ͍ͱ͍͏ҙຯɻ • ໋໊ɾσΟϨΫτϦͷΓํΘΓͱ;Μ͍͖ɻ • ڭՊॻతʹ interfaces/handler
ͱ͔͕ͩɺinterfaces͕όϦΤʔγϣϯʹΉҹ͕͋·Γ ͳ͘ɺtopϨϕϧͷσΟϨΫτϦ͕1ͭ2ͭ૿͑ͨͱͯࠔΔ͜ͱͳ͍ͷͰtopʹhandlerͰ ͖ͬͨɻ·͊ͲͬͪͰ͍͍͔ͳɻ • application͡Όͳͯ͘usecase͚ͬͯͭΔ͜ͱ͕ଟ͍ؾ͢Δɻ ݸਓతʹusecaseͬͯଧͭͱࠨख͕ർΕΔ͔ΒڭՊॻΛ໔ࡑූʹapplicationʹͨ͠ɻ
ઃܭͷझࢫ(Ά͑Ή) • DDD/ΞʔΩςΫνϟΛݫີʹकΖ͏ͱ͗͢͠Δͱɺࣗͨͪʹͱͬͯ ඞཁͰແ͍ෳࡶ͞Λ࣋ͪࠐΜͰ͠·ͬͨΓɺӨڹͷͳ͍ࣄฑʹ͍ͭͯ ʮͲ͜ʹॻ͖͔͘ʯʮͲ͏ॻ͖͔͘ʯͰաʹΜͰ͠·͍͕ͪɻ • ͕ࣗͨͪकΓ͍ͨͷΛकΔ͖Ͱ͋ͬͯɺڭՊॻతͳ໋໊ɾߏΛ ݫक͢Δ͜ͱ͕తͰͳ͍ɻ • कΔ͖͜ͱΛ໌֬ʹɻ
·Δ͖͜ͱ(࣮ͷ੍) 1. domainapp,infra,handlerΛࢀর͠ͳ͍ 1. valuemodel,serviceΛࢀর͠ͳ͍ 2. modelserviceΛࢀর͠ͳ͍ 2. appinfra,handlerΛࢀর͠ͳ͍
੍Λ·ΔͨΊʹ 1. framework, httpʹؔ͢ΔॲཧhandlerͰ݁ͤ͞Δ 2. ϓϩάϥϜͷ֎ͷग़དྷࣄΛҙࣝ͢Δ࣮infraͰ݁ͤ͞Δ 1. ࣮ߦڥ,֎෦API,DB,... 3. appԼ(domain)ʹఆٛͨ͠interfaceʹґଘͤ͞Δ(DIP)
4. ͦΕҎ֎ͷࡉ͔͍͜ͱΑ͠ͳʹɻࠔΔਓ͕͍ͳ͚ΕΑ͍ɻ
੍ʹΑͬͯಘΔͷᶃ • domainʹ֎෦APIDBΛҙࣝ͢Δهड़͕શ͘ͳ͘ͳΔɻ • ७ਮͳϏδωεϩδοΫ͚͕ͩଘࡏ͢ΔͷͰͱ͖ͯΕ͍(খฒײ) • γϯϓϧͳunit testΛ؆୯ɾཏతʹॻ͚Δɻ • DBͷσʔλΛͲ͏༻ҙͨ͠ΒΑ͍͔?localCIͰڥͷࠩແ͍͔?ͲΜ
ͳϦΫΤετ͕དྷΔ͔?ͳͲͷٕज़త/ڥతͳ੍,ςετͷ͔͠͞Βղ์ ͞Ε͍ͯΔͣɻ • mockෆཁͳͷͰɺʮςετͷΓํΛؒҧ͏ʯݒ೦ͳ͍
੍ʹΑͬͯಘΔͷᶄ • applicationrepositoryͷinterfaceͷΈʹґଘ͠infraʹґଘ͍ͯ͠ͳ͍ɻ • infraΛࠩ͠ସ͑Δࣄ͕Ͱ͖Δɻ • mockΛࠩ͠ࠐΜͰͷunit test͕Մೳ • ͜ͷ߹ɺྫ͑ʮσʔλ͕ਖ਼͘͠อଘ͞ΕΔ͔ʯͷςετͰͳ͘ɺʮσʔλΛਖ਼͘͠ΈཱͯͯอଘΛݺΔ͔ʯͷςετʹͳΔ
• ࣮ࡍʹʮDBʹॻ͖ࠐ·ΕΔ͔·Ͱ֬ೝ͍ͨ͠ʯͱ͍͏Θ͔Δ͕ʮapplication୯ମͷͷςετʯͱͯ͜͠ΕͰΑ͍ͣɻ infra࣮͚͕ͩbugͬͨ߹ʹɺapp͕bug͍ͬͯͳ͍͜ͱ͕อূ͞ΕΔɻ • localͰ͑ͳ͍APIΛμϛʔʹࠩ͠ସ͑ͯಈ͔ͨ͠Γ͢Δ͜ͱՄೳ
੍ʹΑͬͯಘΔͷᶅ • infra/handlerʹϏδωεϩδοΫ͕͍ͳ͍ͷͰɺٕज़తͳؔ৺ (http,sql,...)ʹϑΥʔΧεͰ͖Δɻ • ͨͱ͑handlerͯ͢ҎԼͷΑ͏ͳըҰతͰബ͍࣮ʹͳΔ • HTTP RequestͷParse •
applicationݺग़ • Responseฦ٫
αʔϏεͷΓग़͕͠Ͱ͖ͦ͏͔? • app/domain͕͓ޓ͍ʹઐ༻ͱͳ͍ͬͯΔ(ଞͷdomainΛΘͣɺଞͷapp͔ΒΘ Εͳ͍)ͷɺΘ͔ͣͳൣғͰ͔͠ڞ༗͍ͯ͠ͳ͍࣮͕͍͔ͭ͘ੜͨ͡ɻ • ͜ͷ༷ͳՕॴ࣮తʹಠཱੑ͕͔͍ͨɻ • ςετॻ͚͍ͯΔͷͰΓग़͠༰қɻ • Ұ࿈ͷinfra/handler/app/domainΛ·ΔͬͱΓग़ͯ͠ϚΠΫϩαʔϏεʹͯ͠
͠·ͬͯΑͦ͞͏ɻ • ͦ͏Ͱͳ͍Օॴ҆қʹΓग़ͦ͏ͱߟ͑ͣɺ·ͣϦϑΝΫλϢʔεέʔεͷ ཧΛ৻ॏʹਐΊΔͷ͕Α͍Α͏ʹࢥ͏ɻ
Α·
Α·: repositoryϑΝΠϧΘ͚ͳ͍ͷ? • repositoryͬͯinterfaceཏྻ͢Δ͚ͩͰத͕΄΅ͳ͍ͷͰϑΝΠϧ ཚཱͤͯͪ͞ΐͬͱڏ͍͔͠ͳ...ͱ͓ͬͯ·ͱΊ͍ͯΔɻ ָͩ͠ɻ • domain/repository/ Ͱ͖ͬͨ΄͏͕ਖ਼͍͠ͱࢥ͏
Α·: handlerͷςετ • applicationhandlerͷinterfaceԽ͍ͬͯͳ͍ • ͬͨ΄͏͕handler,routingͷUT͍͢͠...͔ͳ? • (ബ͍࣮ͳΒ) handlerͷςετUTΑΓe2eΛؤுͬͨ΄͏͕Α͍ͱࢥ͍ͬͯΔɻ •
handler, ࣮ࡍͷreq/resͰࢼͨ͘͠ͳΔͷ͕ਓͰ͋Γ·ͤΜ͔ • ʮinfraͷҟৗʹର͢ΔhandlerͷڍಈͷςετΛৗʹಈ͔͍ͨ͠ʯͱ͔ͳΒඞཁͦ͏ɻ • mockࠩ͠ࠐΜͰαʔόʔ্͛ͯe2eͰҰಈ࡞֬ೝ͢Εݸਓతʹຬ
Α·: handlerͷςετ • ͪͳΈʹe2eͱ͍ͬͯͦΜͳʹϋΠΧϥͳͷͰͳ͍ɻ • postmanͰಈ࡞֬ೝͯ͠ઃఆΛอଘ͠ɺnewmanͰ͑ΔΑ͏ʹ͍ͯ͠Δɻ • ։ൃதʹʮͪΐͬͱcurlͯ͠ΈΔʯΘΓʹ࡞ͬͯͨΊ͓͚ͯಛʹ࣌ؒΛ͔ͭ Θͳ͔ͯ͘ͳΓͷཏੑʹͳΔͱࢥ͏ɻ
Α·: gomockͱDDD • gomock • https://github.com/golang/mock • InterfaceΛॆ͢Δmock࣮Λࣗಈੜͯ͘͠ΕΔπʔϧ • ʮInterfaceΛॻ͔ͳ͚ΕͳΒͳ͍ʯDDD࣮ͱ૬ੑ͕Α͍
• repositoryΛࢦఆͯ͋͛͠Εmock͕Ψοͱ࡞ΒΕΔ • ʮͲͷΑ͏ʹrepository͕ݺΕͯ΄͍͔͠ʯΛࢦఆͯ͠ςετͰ͖Δ
Α·: gomockͱDDD func TestGetProduct(t *testing.T) { ctrl := gomock.NewController(t) defer
ctrl.Finish() // ੜ͞Εͨmock package m := mock_repo.NewMockProductRepository(ctrl) /* * Get("hoge")͕call͞ΕΔ͜ͱΛςετ͠ɺ * ݺΕͨ߹ID:"hoge"ͷmodelΛฦͯ͘͠ΕΔ * / id := "hoge" m.EXPECT().Get(id).Return(&model.Product{ID: id}, nil) // mockRepoͷIFΛຬ͍ͨͯ͠ΔͷͰinfraͱͯ͑͠Δ app := application.NewProduct(m) res, err := app.GetProduct(id) ... }
Α·: testͱtime.Now() • goʹݶͬͨ/େͨ͠Ͱͳ͍Ͱ͕͢... • ͍Ζ͍Ζͳͱ͜Ζʹ time.Now() ͕͋ΔͨΊʹ҆ఆͨ͠ςετ͕ॻ͚ͣ ʹఘΊͨܦݧ͋Γ·ͤΜ͔ •
ςετ͍͢͠ΞʔΩςΫνϟͰؤுͬͯॻ͍ͯɺͦ͜Ͱͭ·͍ͣͯ ҙຯ͕͋Γ·ͤΜɻ • ࣌ؒউखʹॻ͖ΘΓଓ͚ΔglobalมͳͷͰॳظஈ֊͔Βੵۃతʹ ͍ग़͓ͯ͘͠ͷ͕ྑ͍ͱࢥ͍ͬͯ·͢ɻ
Α·: testͱtime.Now() package clock import "time" // Clock ࣌ؒͷinjection༻IF type
Clock interface { Now() time.Time } // RealClock ࣮࣌ؒΛฦ͢. αʔόʔ্Ͱappʹ͜ΕΛ͢. type RealClock struct {} func (r *RealClock) Now() time.Time { return time.Now() } func NewRealClock(loc *time.Location) *RealClock { return &RealClock{} } // MockClock ݻఆ࣌ؒΛฦ͢μϛʔ࣌ܭ.ςετͰappʹ͜ΕΛ͢. type MockClock struct { now *time.Time } func NewMockClock(t *time.Time) *MockClock { return &MockClock{now: t} } func (m *MockClock) Now() time.Time { return time.Now() } timeΛ͍ग़͢Ұͭͷྫ. ͕࣌ؒඞཁͳapplicationʹ࣌ܭ(Now ΛऔΕΔϞϊ)Λ͢Α͏ʹ͓ͯ͘͠ɻ ࣌ܭ͢ͷΊΜͲ͍ͱ͍͏ͷ͋Δ͕࣌ؒͷґ ଘΛ໌ࣔతʹ͢ΔҙຯͰଥͩͱࢥ͍ͬͯΔɻ ʘ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʗ
Αͨͳ͠
infraͷࠩ͠ସ͑,ϚϧνΫϥυͱ͍͏ເ • ʮinfraҎԼʹٕज़తؔ৺Λด͡ࠐΊΔͱσʔλϕʔεج൫ΛҠߦ͠Α͏ͬͯͳͬͨ࣌ʹָʯ • ࣮ࡍͱͯͦ͠Μͳͷ͕ඞཁʹͳΔ͜ͱͳ͍Ζʁͬͨ͜ͱ͋Δ͓ͭΓΎʙ? • ͬͨɻ • ࣾͷٕज़ϙʔτϑΥϦΦଟ༷ԽݱߦͷΠϯϑϥʹ͕ੜͨ͡߹ʹඋ͑ͯɺ৽͍ٕ͠ज़ͷݕূͯ͠Έͨ ͍ΑͶͱ͍͏͕͋ͬͨɻ
• PJͷλΠϛϯάܦݧతͳ߹͋ͬͯɺGCP/CloudDatastoreΛར༻ͯ͠MVP࣮Λߦͬͨɻ • ͦͷޙॾʑͷࣄΛצҊͯ͠AWS/Auroraʹࡌͤସ͑ͨɻ • ্࣮ͷӨڹൣғ͔֬ʹinfraҎԼʹݶఆ͞Εɺॻ͖͑ࣗମ2͔͔Βͳ͍͘Β͍ͰࡁΜͩɻ • (࣮͠ͳ͍Ͱࣄલͷݕ౼Ͱશ෦ચ͍ग़ͤΑɺͱݴΘΕΔͱ͕ࣖ௧͍ͱ͜ΖͰ͋Δ...·͊ૉৼΓͨ·ʹͶ?)