Slide 1

Slide 1 text

GopherCon 2019 Report mercari.go #10 @upamune

Slide 2

Slide 2 text

About Me @upamune (うぱみゅん) Merpay, Inc. / CodePayment Team Backend Engineer

Slide 3

Slide 3 text

発表するトーク

Slide 4

Slide 4 text

How I Write HTTP Web Services After Eight Years Mat Ryer 出典: https://medium.com/@matryer

Slide 5

Slide 5 text

Who is Mat Ryer?

Slide 6

Slide 6 text

Mat Ryer Blog medium.com/@matryer Podcast Go Time Books Go ⾔語による Web アプリケーション開発 Go Programming Blueprints OSS BitBar Testify Gopherize.me etc...

Slide 7

Slide 7 text

出典: GoTime https://cdn.changelog.com/uploads/covers/go-time-medium.png Go⾔語によるWebアプリケーション開発 https://www.amazon.co.jp/dp/4873117526 Go Programming Blueprints https://www.amazon.co.jp/dp/B01GQCQ8OW

Slide 8

Slide 8 text

トーク概要

Slide 9

Slide 9 text

トーク概要 Go で HTTP service をどう書いているか HTTP service を書く時に重要な要素を紹介 いくつかのパターンを紹介

Slide 10

Slide 10 text

重要な要素

Slide 11

Slide 11 text

重要な要素 Maintainability Glaceability Code should be boring Self Similar code

Slide 12

Slide 12 text

Maintainability 最初から Maintainability を考慮して書く 考慮しないと、メンテナンスコストがツールを作る時よりも⼤きくなる可能性

Slide 13

Slide 13 text

Glaceability 視認性 コードを読んでどれくらい早くコードを理解できるか コードだけではなく、プロジェクト構造なども含まれる 関数、名前空間、変数名、コードの構造、etc...

Slide 14

Slide 14 text

Code should be boring 他の⼈が理解できるように書く 経験のほとんどない⼈がコードを利⽤する可能性があることを理解するのが重要

Slide 15

Slide 15 text

Self Similar code コードベース内の他のコードに似たコードがあると、 他の⼈がコードに親しみやすくなる

Slide 16

Slide 16 text

Design Patterns/Decisions

Slide 17

Slide 17 text

全部紹介しきれないのでいくつかピックアップ

Slide 18

Slide 18 text

Creating a server struct & a constructor for the server type server struct { db *someDatabase router *someRouter email EmailSender } func newServer() *server { s := &server{} s.routes() return s } グローバル変数は利⽤しない それを避けるために server 構造体が持つ newServer では依存をセットアップしない テストのため 多くなければ引数でとっても良い ルーティングだけセットアップする

Slide 19

Slide 19 text

Routing // routes.go func (s *server) routes() { s.router.Get("/api/", s.handleAPI()) s.router.Get("/about", s.handleAbout()) s.router.Get("/", s.handleIndex()) } ⼀箇所でルーティングを管理する 視認性 ⤴ URL からどのハンドラーを利⽤しているか容易に特定できる

Slide 20

Slide 20 text

Dealing with data // Respond helper func (s *server) respond(w http.ResponseWriter, r *http.Request, data interface{}, status int) { w.WriteHeader(status) if data != nil { err := json.NewEncoder(w).Encode(data) // TODO: handle error } } // Decoding helper func (s *server) decode(w http.ResponseWriter, r *http.Request, v interface{}) error { return json.NewDecoder(r.Body).Decode(v) } 抽象化する 後から Accept ヘッダーや Content-Type ヘッダーに対応することが容易に ヘルパーは http.ResponseWriter と *http.Request を引数で受け取る

Slide 21

Slide 21 text

Request and response func (s *server) handleGreet() http.HanlderFunc { type request struct { Name string `json:"name"` } type response struct { Greeting string `json:"greeting"` } return func(w http.ResponseWriter, r *http.Request) { // do something... } } Handler に関連する Request/Response が⾒つけやすい 関数の中で定義しているので request , response という短い構造体名にできる

Slide 22

Slide 22 text

Request and response func TestGreet(t *testing.T) { is := is.New(t) p := struct{ Name string `json:"name"` }{ Name: "Yu SERIZAWA", } // ... test code } テストの際は request 構造体を参照できないので、リクエストの struct を定義する Name フィールドだけこのテストでは関係するとわかる

Slide 23

Slide 23 text

Lazy setup func (s *server) handleTemplate(file string...) http.HandleFunc { var ( init sync.Once tpl *template.Template tplerr error ) return func(w http.ResposeWriter, r *http.Request) { init.Do(func() { tpl, tplerr = template.ParseFiles(files...) }) if tplerr != nil { // return error } // use template } } 重い処理を呼ばれるまで sync.Once で遅らせる GAE を利⽤する場合などで起動時間を速くしたい場合に有効

Slide 24

Slide 24 text

残りは元スライド、ライブブログで

Slide 25

Slide 25 text

matryer/is is ... ? I call it “Testify off steroids” :) https://gophers.slack.com/archives/C0528UE9X/p1564348834304100?thread_ts=1564339225.294700&cid=C0528UE9X

Slide 26

Slide 26 text

matryer/is func Test(t *testing.T) { is := is.New(t) signedin, err := isSignedIn(ctx) is.NoErr(err) // isSignedIn error is.Equal(signedin, true) // must be signed in body := readBody(r) is.True(strings.Contains(body, "Hi there")) } https://github.com/matryer/is#usage

Slide 27

Slide 27 text

トークの感想 だいたい似たよう感じに作っているので安⼼できた 業務で HTTP sevice を作ることはほとんどないが、 HTTP service 以外を書く時でも 参考にできることがあった 最初に挙げていた重要な要素など testify を置き換えたい

Slide 28

Slide 28 text

参考リンク Slide https://gophers.slack.com/archives/C0528UE9X/p1564339225294700 このレスに is の説明もあります 元になったブログ記事 https://medium.com/statuscode/how-i-write-go-http-services-after-seven- years-37c208122831 LiveBlog https://about.sourcegraph.com/go/gophercon-2019-how-i-write-http-web- services-after-eight-years