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

Golang再入門

 Golang再入門

* Golangでのinterfaceの使い方
* エラーハンドリング

ques0942

June 20, 2023
Tweet

Other Decks in Programming

Transcript

  1. 1
    Golang再入門
    mercari.go #22

    View full-size slide

  2. 2
    自己紹介
    ● twitter: @ques0942
    ● mercariのバックエンドエンジニア
    ● 最近の主戦場はPHPで、mercariの取引機能を担当するチームで働いています
    ● 1年半ほど前まではGolangがメインで、最近Golangに再入門しました

    View full-size slide

  3. 3
    今日話すこと
    ● Golang再入門するときに勉強し直したこと、変化を認識したことの紹介
    ○ 初心者向けの内容です
    ● 目次
    ○ 構造的部分型(Golangのインターフェースの使い方 )
    ○ エラーハンドリング

    View full-size slide

  4. 4
    構造的部分型(Golangのインターフェースの使い方)
    ● PHPなどの公称型とは異なる型システム
    ○ クラスではなくインターフェースに基づいて型チェックがなされる
    ○ Pythonのduck typingを静的に行うようなもの
    ○ 継承、抽象クラスは無いが、構造体を埋め込んで機能を流用することは可能
    ● Golang(とかTypeScriptとかClean architecture)に慣れると「機能を使う側が
    インターフェースを定義する」という感覚になった
    ○ Javaでコードを書いていたときは、インターフェースは実装しているクラスの付属物だと思っていた
    ■ クラスを作る側がインターフェースを定義する
    ○ “Accept interfaces, return structs” とか github/goのwikiにあるレビュー指針 とかを参考に
    するとインターフェースは呼び出す側のものとして整理されている

    View full-size slide

  5. 5
    構造的部分型(Golangのインターフェースの使い方)
    ● 例えばAPIを書くとして、あるリクエストをコントローラーが処理するときにストレージ
    への読み込み、書き込みが発生する
    ● このストレージのインターフェースはコントローラーとデータストア、どちらのレイヤー
    が定義する?
    ● サンプルコード

    View full-size slide

  6. 6
    構造的部分型(Golangのインターフェースの使い方)
    ● 例えば以下のようなパッケージ構
    成のとき
    ○ cmd/main.go
    ○ controller/controller.go
    ○ storage/storage.go
    ● 右のインターフェースはどこに置く?
    type StringStorage interface {
    Load(key string) (string, error)
    Store(key, value string) error
    }

    View full-size slide

  7. 7
    構造的部分型(Golangのインターフェースの使い方)
    package controller
    type StringStorage interface {
    Load(key string) (string, error)
    Store(key, value string) error
    }
    type Controller struct {
    s StringStorage
    }
    func New(s StringStorage) *Controller {
    return &Controller{s: s}
    }
    func (c *Controller) Load(key string) (string, error) {
    return c.s.Load(key)
    }
    func (c *Controller) Store(key, value string) error {
    return c.s.Store(key, value)
    }
    ● 今のところインター
    フェースを利用する
    controllerパッケージ
    に置くのが良いと思って
    います

    View full-size slide

  8. 8
    構造的部分型(Golangのインターフェースの使い方)
    ● 依存する機能(メソッド)が明確になる
    ○ 例えばStringStorageにReplace(key, value)みたいな機能が増えても、 Controllerはその機能
    を使うまではインターフェースを変更する必要がない
    ○ インターフェースに含まれない関数は利用されないので、機能改修時に影響範囲調査が楽になる
    ● 抽象度の高い方から実装できる
    ○ ユースケースに近いところから実装
    ■ 必要なインターフェースを定義
    ● ストレージとかAPIクライアントをそのインターフェースに合うように実装
    ○ 契約駆動開発(Contract Driven Development)味がある
    ○ 結局うまく実装できなくて抽象度の高い側を直す羽目になったりもしますが

    View full-size slide

  9. 9
    エラーハンドリング
    ● 昔はpkg/errorsで良かったけど今は?
    ● 欲しい機能
    ○ 任意のエラーを定義して何が起きたかを特定したい
    ○ スタックトレースを取得して、意図しないエラーがどこでどのように発生したか特定したい
    ● 検討した候補
    ○ errors.New + fmt.Errorf
    ■ golang1.13からの機能
    ■ スタックトレースを保存する機能がない
    ○ pkg/errors
    ■ おなじみだがアーカイブされた
    ○ morikuni/failure
    ■ mercari社内で利用実績あり

    View full-size slide

  10. 10
    エラーハンドリング

    View full-size slide

  11. 11
    欲しい機能(独自エラー)
    ● アプリケーション独自のエラーを定義したい
    ○ 例えば
    ■ UserNotFoundError
    ■ PermissionDeniedError
    ■ InvalidValueError
    ● なぜ?
    ○ アプリケーションでどんなトラブルが起きたか知りたい
    ○ 適切なエラーハンドリングがしたい
    ■ 発生したエラーを識別して、ユーザーに伝える or 適切な緩和処置をしたい

    View full-size slide

  12. 12
    欲しい機能(スタックトレース)
    ● エラーには発生したときのスタックトレースが入っていてほしい
    ● なぜ?
    ○ エラーテキストは考慮漏れで重複する可能性が有り得る
    ○ どのような経路で呼び出されたか知りたい

    View full-size slide

  13. 13
    現時点での私の結論
    ● アプリケーションで利用するなら
    ○ morikuni/failure or pkg/errors
    ■ どちらも任意のエラーコード定義とスタックトレースの取得が可能
    ■ 個人的には
    ● 仕事ではmorikuni/failures
    ● 個人開発では使い慣れた pkg/errorsという感じ
    ■ pkg/errorsはアーカイブされたとはいえそんなに機能追加がいるものでは無いので …
    ● と思っていたらgo1.20でerrorsに複数エラーをラップできる 新機能が
    ● スタックトレースが要らないなら
    ○ errors.New + fmt.Errorf

    View full-size slide