Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Goでやや大きいプロダクトのアーキテクチャを検討したり実装したりしたおはなし

Avatar for yami20 yami20
July 17, 2020

 Goでやや大きいプロダクトのアーキテクチャを検討したり実装したりしたおはなし

Avatar for yami20

yami20

July 17, 2020
Tweet

Other Decks in Technology

Transcript

  1. ཁ݅ɾن໛ɾಛੑ ʙอݥϓϥοτϑΥʔϜγεςϜʙ •(͜ͷล͸ࢀߟఔ౓ʹॻ͍͚ͨͩͳͷͰඈ͹͠·͢) • ཁ݅ • APIαʔόʔ(SPAͷbackend) • ͪͳΈʹରʹͳΔfrontendͷ͸ͳ͠͸ https://speakerdeck.com/slont/bokufalsekangaetasaikiyoufalsevueakitekutiya

    • ෳ਺ͷอݥձ͕ࣾ঎඼Λઃܭɾొ࿥͠ɺސ٬ͷਃࠐΛड͚෇͚ͨΓɺܖ໿৹ࠪͨ͠Γ؅ཧͨ͠Γɺͦͷଞ෇ਵ͢Δػೳ͕΋ʹΐ΋ʹΐɻ • ن໛ • ΞυςΫ΍ήʔϜʹൺ΂ΔͱτϥϑΟοΫ͸֨ஈʹ͓ͱͳ͍͠ɻ1ਓ͕1೔ʹԿेճ΋Ճೖͨ͠Γ͠ͳ͍͠ɺ࿈ଧͯ͠౗͢ఢ΋͍ͳ͍ɻ • PFͰ͋Δ͜ͱΛ౿·͑ͯ΋ҰൠతͳECͱ͔ͷن໛ײΛΠϝʔδ͍͚ͯͨͩ͠Ε͹ɻ • ಛੑ • σʔλͷϥΠϑαΠΫϧ͕௕͍ɻ೥୯Ґ͕جຊɺਓੜ୯ҐͷऔҾ΋͋Δ • σʔλͷߏ଄͕ෳࡶ. ଐੑ΋ϦϨʔγϣϯ΋ͱʹ͔͘ଟ͍. • γεςϜͷण໋͕௕͍ɻσʔλͷϥΠϑαΠΫϧ͕௕͍ͷ΋͋Δ͠ɺPFͳͷͰ͓͍ͦΕͱด͡ΒΕͳ͍ͱ͍͏ͷ΋͋Δɻ • PFͰ͋ΔͨΊɺॳظʹग़Δཁ͕݅͢΂ͯͱ͸ݶΒͳ͍ • ࢥ͍΋ΑΒͳ͍֦ு͕ඞཁʹͳΔՄೳੑ͕͋Δ
  2. ϞϊϦεͱϚΠΫϩαʔϏε • ϞϊϦεʹ਌Λࡴ͞Εͨܦݧ͸͋Δɻ • ϑϩϯτ͔Β؅ཧը໘·Ͱ͢΂͕ͯಉࠝ͞Εɺ100ਓҎ্ͷ։ൃऀ͕ҰͭͷϦϙδτϦʹ commit͠ʮdeployฒͼ·͢ʯͱ੠Λ͔͚߹͍ɺσϓϩΠࣦഊ͢Δͱଞ෦ॺʹౖΒΕΔσΟ ετϐΞ • ϚΠΫϩαʔϏεʹଜΛম͔Εͨܦݧ΋͋Δɻ •

    1~3ςʔϒϧ૬౰ͷσʔλຖʹઐ༻ͷAPI͕ཚཱͯ͠૬ޓʹԿ౓΋ࢀর͍͋͠ɺ͍ͭͲ ͜ͰԿͷॲཧ͕ى͖͍ͯΔ͔Θ͔Βͳ͍σΟετϐΞ • ݁ہɺ͓࡞๏ʹࠩͷ͋Δଟ਺ͷγεςϜΛ͢΂ͯ೺Ѳ͠ͳ͚Ε͹Կ΋Θ͔Βͳ͍ɻ
  3. ϞϊϦεͱϚΠΫϩαʔϏε • ϞϊϦε • ϝϯςෆՄೳͳ΄Ͳίʔυɾؔ܎ऀ͕ଟ͘ͳΔͱͭΒ͍ • ಉҰϦϙδτϦ಺͔ͩΒͱແࠩผʹࢀরΛΏΔ͢ͱΘ͚Θ͔ΒΜ͘ͳΔ • ϚΠΫϩαʔϏε •

    ෆద੾ͳ෼ׂʹΑͬͯຊདྷඞཁͷͳ͍ॲཧ͕૿Ճ͢Δͱͱͯ΋ͭΒ͍ • Ұൠతʹ࣮૷ྔ͸૿͑Δ͠ɺΠϯϑϥߏ੒໘Ͱͷݕ౼ࣄ߲ɾઃఆ΋૿͑Δ
  4. ϞϊϦεͱ͍͏બ୒ • ࠷దͳϚΠΫϩαʔϏεͷཻ౓Λࣄલʹݟग़͢͜ͱ͸ඇৗʹ೉қ౓͕ߴ͍ɻ • ࢓༷௨Γʹ࣮૷͍ͯͨ͠ͷʹʮ࣮͸Aͷσʔλ͚ͩͩͱ଍Γͳͯ͘ɺৗʹBͱC΋ҰॹʹཁΔΜͩ...ʯͱ͔ݴΘΕ͕ͪ • Goͬͯهड़ྔࣗମ͸গͳ͘ͳ͍Ͱ͢ΑͶ? (ڞײΛٻΊΔѹྗ) • γϯϓϧͰಡΈॻ͖͠΍͍͢෼ɺʮָΛͤͯ͘͞ΕΔʯ࢓૊Έ͸߇͑Ίɻ

    • ϚΠΫϩαʔϏεͰͷهड़૿Ճͱ߹ΘͤΔͱͪΐͬͱϔϏʔɻ • αʔϏεͷ্ཱͪ͛࣌఺ͰϞϊϦε๊͕͑Δ໰୊͕ੜ͡ΔϦεΫ͸খ͍͞ • ʮϞϊϦεͷ··ʯҭͯͳͯ͘͢ΉΑ͏ʹద੾ͳઃܭɾ੍໿Λ͠ɺ͕࣌དྷͨΒ෼ׂ͢Ε͹Α͔Ζ͏ͳ ͷͩ • αʔϏεΛҭ͍ͯͯ͘தͰద੾ͳ෼ׂཻ౓͕ݟ͑ͯ͘Δͱ͍͏ϝϦοτ΋͋Δɻ
  5. ґଘੑͷٯస(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) } ```
  6. ϨΠϠʔυΞʔΩςΫνϟ෩ʹຬͨ͢ͱ... ```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. ... } ```
  7. ΫϦʔϯΞʔΩςΫνϟ෩ʹຬͨ͢ͱ... ```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.... } ```
  8. ઃܭͷझࢫ • ࣮ફDDDʹ͍͏ʮ؇΍͔ͳϨΠϠʔԽΞʔΩςΫνϟʯ͕ϕʔε • ʮ؇΍͔ʯͱ͸ɺ௚Լͷ૚͚ͩͰͳ͘Լํ޲ͷ૚͸͍ͣΕ΋࢖ͬͯΑ͍ͱ͍͏ҙຯɻ • ໋໊ɾσΟϨΫτϦͷ੾Γํ͸ΘΓͱ;Μ͍͖ɻ • ڭՊॻతʹ͸ interfaces/handler

    ͱ͔͕ͩɺinterfaces͕όϦΤʔγϣϯʹ෋Ήҹ৅͕͋·Γ ͳ͘ɺtopϨϕϧͷσΟϨΫτϦ͕1ͭ2ͭ૿͑ͨͱͯࠔΔ͜ͱ΋ͳ͍ͷͰtopʹhandlerͰ ͖ͬͨɻ·͊ͲͬͪͰ΋͍͍͔ͳɻ • application͡Όͳͯ͘usecase͚ͬͯͭΔ͜ͱ͕ଟ͍ؾ΋͢Δɻ
 ݸਓతʹusecaseͬͯଧͭͱࠨख͕ർΕΔ͔ΒڭՊॻΛ໔ࡑූʹapplicationʹͨ͠ɻ
  9. ੍໿ʹΑͬͯಘΔ΋ͷᶄ • application͸repositoryͷinterfaceͷΈʹґଘ͠infraʹ͸ґଘ͍ͯ͠ͳ͍ɻ • infraΛࠩ͠ସ͑Δࣄ͕Ͱ͖Δɻ • mockΛࠩ͠ࠐΜͰͷunit test͕Մೳ • ͜ͷ৔߹ɺྫ͑͹ʮσʔλ͕ਖ਼͘͠อଘ͞ΕΔ͔ʯͷςετͰͳ͘ɺʮσʔλΛਖ਼͘͠૊ΈཱͯͯอଘΛݺ΂Δ͔ʯͷςετʹͳΔ

    • ࣮ࡍʹʮDBʹॻ͖ࠐ·ΕΔ͔·Ͱ֬ೝ͍ͨ͠ʯͱ͍͏࿩͸Θ͔Δ͕ʮapplication୯ମͷ੹຿ͷςετʯͱͯ͠͸͜ΕͰΑ͍͸ͣɻ infra࣮૷͚͕ͩbugͬͨ৔߹ʹ΋ɺapp͕bug͍ͬͯͳ͍͜ͱ͕อূ͞ΕΔɻ • localͰ࢖͑ͳ͍APIΛμϛʔʹࠩ͠ସ͑ͯಈ͔ͨ͠Γ͢Δ͜ͱ΋Մೳ
  10. Α΋΍·: handlerͷςετ • application΍handlerͷinterfaceԽ͸΍͍ͬͯͳ͍ • ΍ͬͨ΄͏͕handler,routingͷUT͸͠΍͍͢...͔ͳ? • (ബ͍࣮૷ͳΒ͹) handlerͷςετ͸UTΑΓe2eΛؤுͬͨ΄͏͕Α͍ͱࢥ͍ͬͯΔɻ •

    handler, ࣮ࡍͷreq/resͰࢼͨ͘͠ͳΔͷ͕ਓ৘Ͱ͸͋Γ·ͤΜ͔ • ʮinfraͷҟৗʹର͢ΔhandlerͷڍಈͷςετΛৗʹಈ͔͍ͨ͠ʯͱ͔ͳΒඞཁͦ͏ɻ • mockࠩ͠ࠐΜͰαʔόʔ্͛ͯe2eͰҰ౓ಈ࡞֬ೝ͢Ε͹ݸਓతʹ͸ຬ଍
  11. Α΋΍·: 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) // mock͸RepoͷIFΛຬ͍ͨͯ͠ΔͷͰinfraͱͯ͠࢖͑Δ app := application.NewProduct(m) res, err := app.GetProduct(id) ... }
  12. Α΋΍·: testͱtime.Now() • goʹݶͬͨ࿩/େͨ͠࿩Ͱ͸ͳ͍Ͱ͕͢... • ͍Ζ͍Ζͳͱ͜Ζʹ time.Now() ͕͋ΔͨΊʹ҆ఆͨ͠ςετ͕ॻ͚ͣ ʹఘΊͨܦݧ͸͋Γ·ͤΜ͔ •

    ςετ͠΍͍͢ΞʔΩςΫνϟͰؤுͬͯॻ͍ͯ΋ɺͦ͜Ͱͭ·͍ͣͯ ͸ҙຯ͕͋Γ·ͤΜɻ • ࣌ؒ͸উखʹॻ͖׵ΘΓଓ͚Δglobalม਺ͳͷͰॳظஈ֊͔Βੵۃతʹ ௥͍ग़͓ͯ͘͠ͷ͕ྑ͍ͱࢥ͍ͬͯ·͢ɻ
  13. Α΋΍·: 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 ΛऔΕΔϞϊ)Λ౉͢Α͏ʹ͓ͯ͘͠ɻ ౎౓࣌ܭ౉͢ͷΊΜͲ͍ͱ͍͏ͷ͸͋Δ͕࣌ؒ΁ͷґ ଘΛ໌ࣔతʹ͢ΔҙຯͰ͸ଥ౰ͩͱࢥ͍ͬͯΔɻ
 
 ʘ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʗ
  14. infraͷࠩ͠ସ͑,ϚϧνΫϥ΢υͱ͍͏ເ • ʮinfraҎԼʹٕज़తؔ৺Λด͡ࠐΊΔͱσʔλϕʔε΍ج൫ΛҠߦ͠Α͏ͬͯͳͬͨ࣌ʹָʯ • ࣮ࡍ໰୊ͱͯͦ͠Μͳͷ͕ඞཁʹͳΔ͜ͱͳ͍΍Ζʁ΍ͬͨ͜ͱ͋Δ΍͓ͭΓΎʙ? • ΍ͬͨɻ • ࣾ಺ͷٕज़ϙʔτϑΥϦΦଟ༷Խ΍ݱߦͷΠϯϑϥʹ໰୊͕ੜͨ͡৔߹ʹඋ͑ͯɺ৽͍ٕ͠ज़ͷݕূ΋ͯ͠Έͨ ͍ΑͶͱ͍͏࿩͕͋ͬͨɻ

    • PJͷλΠϛϯά΍ܦݧతͳ౎߹΋͋ͬͯɺGCP/CloudDatastoreΛར༻ͯ͠MVP࣮૷Λߦͬͨɻ • ͦͷޙॾʑͷࣄ৘ΛצҊͯ͠AWS/Auroraʹࡌͤସ͑ͨɻ • ࣮૷্ͷӨڹൣғ͸͔֬ʹinfraҎԼʹݶఆ͞Εɺॻ͖׵͑ࣗମ͸2೔͔͔Βͳ͍͘Β͍ͰࡁΜͩɻ • (࣮૷͠ͳ͍Ͱࣄલͷݕ౼Ͱશ෦ચ͍ग़ͤΑɺͱݴΘΕΔͱ͕ࣖ௧͍ͱ͜ΖͰ͸͋Δ...·͊ૉৼΓ΋ͨ·ʹ͸Ͷ?)