every Tech Blog

株式会社エブリーのTech Blogです。

API Serverの新規開発時に導入してみて良かった事

f:id:fkymev:20210511174549p:plain

はじめに

DELISH KITCHEN開発部の福山です。 社内向けシステムとしてAPI Serverを新規に構築する機会がありました。新規開発にあたり導入してみて良かった事をいくつかご紹介したいと思います。

前提技術スタック

新規GitHubリポジトリを用意してREST APIをGoで実装する。Webフレームワークはechoを採用。インフラはAWS ECS、RDSを利用。ログ情報はfluentd経由でS3やTreasuredataに格納しています。SentryやDatadogによる監視も行っています。

pre-commit、CIでのLintチェック、パッケージをクリーンアーキテクチャ構成にする

pre-commit

『DELISH KITCHEN』のメインAPI Serverの方で途中から採用された内容とほぼ同じ内容となります。今回は実装初期段階でpre-commit を導入しました。 pre-commitを使えばローカルでのGit commit時に任意のスクリプトを実行出来ます。

以下の処理が行われる様に設定されています。

- go generate
- go vet
- gofmt
- goimports
- golint
- wire

主に実装内容の静的解析を行っておりcommit前にルールから外れたコードを発見し修正を促します。 その他go generate契機でmockファイルの作成や wire でDIコードの生成処理(*後述)を実行しています。

良かった事

コードレビュー時にレビュアーがコードフォーマットや生成ファイルの有無等の指摘をする必要が無くなり、仕様やバグのチェックに集中出来る様になりました。

CIでのLintチェック

自動テストは導入しているのですが、更に実装初期段階からGithub Actionsにて reviewdog/golangci-lint を導入しました。 golangci-lintには 様々なLinter が用意されておりプロジェクト状況に合わせて任意のLinterを利用出来ます。

今回の開発では以下のLintチェックを有効化しています。

- bodyclose
- deadcode
- depguard
- errcheck
- goconst
- gocritic
- gofmt
- goimports
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- misspell
- nakedret
- noctx
- prealloc
- scopelint
- staticcheck
- structcheck
- typecheck
- unconvert
- unparam
- unused
- varcheck

pushした内容に指摘対象のコードが存在する時にGithub上で以下の様に表示してくれます。

f:id:fkymev:20210511162002p:plain
reviewdog/action-golangci-lint

良かった事

pre-commitと同じなのですがレビュアーに指摘される前に自動チェックが行われる為、コードレビュー時に仕様やバグのチェックに集中出来る様になりました。 具体的にはgosecでセキュリティ面での指定が有ったり、gosimpleでシンプルなコードの書き方に気付かされたりとコードの品質向上のきっかけとなります。 特に実装初期段階から導入した事によりほぼ全てのコードが随時チェックされている事になるので途中から採用するよりオススメです。

パッケージをクリーンアーキテクチャ構成にする

既存のシステムでMVC的な構成で苦労する場面が有りました。Goでdomain/model的なパッケージとデータの永続化処理は切り離したいと考え今回はクリーンアーキテクチャ構成で実装する事にしました。
(色々なクリーンアーキテクチャの詳細解釈が存在するのであくまで一例とお考え下さい。)

ざっくり過ぎる図解なのですが以下の様なパッケージ構成となっております。(→は依存方向)

f:id:fkymev:20210511162214p:plain
パッケージレイヤー構成の一例

最低限下記は意識しつつ、ルールに拘り過ぎて工数が掛かり過ぎない様に状況に応じて詳細実装を進めました。

- 依存方向を守る(domainは他に依存しない)
- 抽象に依存する(interfaceに依存する)

handlerパッケージから wire で生成されたコードでDependency Injectionされる構成となっております。 動作のポイントとしてはdomain/repositoryで定義されたinterfaceの詳細実装はinfrastructureに存在しており、注入された内容として実行される様になっています。 他にはWebフレームワークの固有処理もhandler内に閉じておりusecase以降には影響しない様になっています。

良かった事

  • テストの容易性

一般的に言われている事ですがテスタブルになりました。 MVC構成だと要件ロジックのユニットテストを書く時も依存するデータベース情報等を実際に用意する必要が有りました。しかし抽象に依存しつつパッケージレイヤーを切った事により、例えばusecaseでのユニットテスト時にパッケージ内で扱う永続化処理に対してmockを利用し任意の結果が返却出来る事になります。 従ってテスト対象パッケージ以外の状態に悩まされる事無く確認したい要件ロジックに集中してユニットテストを行う事が出来る様になりました。

  • ドメイン情報の認識が深まる

単一パッケージにほぼ全てのロジックを詰める様な形だと肥大したり処理の責務等は考えなくなってしまう可能性が高いのですが、 要件ロジックをどのパッケージレイヤーに書くべきかを個人的にもチーム内でも意識する様になりました。 実際にPRレビューの時にチームメンバーと議論する事が有り、結果として当初より見通しの良いコードになる事が有りました。 この部分は個々人の解釈の違いも有るので工数が膨らみ過ぎないバランスで進める様にしています。

まとめ

新規開発を行うタイミングで導入して良かった事としてpre-commitCIでのLintチェックパッケージをクリーンアーキテクチャ構成にするをご紹介しました。
工数面のバランスや未経験な技術要素を導入するリスクも有りますが開発工程初期に開発効率を向上させる仕組みを用意するメリットは大きいと考えています。 まだ開発は続きますので良い仕組みを活かして効率良くアウトプットしていきたいと思います。