Chapter 04

Go言語への変更とクリーンアーキテクチャーの採用

株式会社var(ヴァー)
株式会社var(ヴァー)
2021.09.26に更新

こちらのチャプターでは、Pythonを利用していたAPIやWebアプリケーションサーバーをGo言語に変更し、クリーンアーキテクチャーGraphQLを採用した経緯を説明します。

これまで、Envaderは以下の2つの機構をPythonを利用して開発していました。

  1. Webアプリケーション機構
  2. Dokcerコンテナを起動・停止するAPI

Webアプリケーションに関しては、REST APIの概念に従って実装を進めていました。

技術選定に関しては、とりあえずプロトタイプを作りたかったので自分たちの使い慣れている言語を利用していたというだけです。
また、Web APIといえばRESTで作るのが一般的だろうという安易な考えでRESTを採用していました。

しかし、Envaderは元々2人のエンジニアで作成しており、まともなドキュメント管理はできていませんでした。一方で、APIの数はますます増えていき、swaggerなどのツールを利用するのも現実的でないとなったため、エンドポイント管理が崩壊を迎えつつありました。

そこで、案としてあがったのがGraphQLです。
GraphQLが、BFF(Backends For Frontends)として機能することにより、フロントとバックの分離をすることが可能になりました。
また、単一エンドポイント(/query)であり、スキーマを定義するため、スキーマが実質ドキュメント代わりになるという我々には魅力的なツールでした。また、フロントエンドにはvue.jsを採用していたのでTypescriptとの相性もいいと判断し、GraphQLを採用しました。

次に、Go言語の採用の背景です。

1つ目の基準は、実行速度です。
Envaderのように、仮想的なサーバー環境(Dockerコンテナ群)を提供するサービスは一般的に待ち時間が多い印象でした。この待ち時間を減らせる言語、つまり実行速度が高速な言語としてGo言語が候補にあがりました。

2つ目の基準は、Dockerとの相性です。
ご存知の方も多いと思いますが、Dockerk8sはGoで書かれているため、Goのライブラリ等の相性がPythonと遜色ない(または上回る)と判断し、Go言語を採用しました。

3つ目の基準は、GAEのランタイムに含まれているかです。
ここは特に解説しません。

以上の考察から、Pythonで書かれていたシステムをGoで書き直しました。
WebフレームワークのFlaskは同じくGoの軽量WebフレームワークであるEchoに切り替わりました。

ここからは、クリーンアーキテクチャー採用の背景を説明します。
理由としては、大きく3つです。

  • これまで、テストの難しさや開発環境と本番環境の差分を埋めることに苦労していた
  • 外側とのデータのやり取りへの依存を減らしたかった
  • コードが冗長的になっており、何かしらのアーキテクチャ設計に則ってプログラムをリファクタリングしたかった
  • なんか最近流行っていた(冗談です。)

以上の結果として、

  • データベースの接続先を柔軟に切り変え可能になった
  • セッション情報の保存場所の切り替えがやりやすくなった
    • (例、本番環境ではRedisを利用するが、ローカルではgo-cacheを利用する)
  • サービス全体の実行速度が上がった

最後に、ディレクトリ構成を示して終わりにします。
正直、これがベストプラクティスなのかわかりませんが、今回はこのような構成にしました。
何か改善点がある方はコメントして頂けると嬉しいです。

.
├── auth
│   └── auth.go // 認証系
├── entity // Entity層
│   ├── entity.go
│   └── errors.go
├── env // GAEのconfig
│   ├── dispatch.yaml
│   ├── production.app.yaml
│   └── devlop.app.yaml
├── go.mod
├── go.sum
├── gqlgen.yml
├── graph
│   ├── generated
│   │   └── generated.go
│   ├── model
│   │   └── models_gen.go
│   ├── resolver.go // Controller層
│   ├── schema.graphqls
│   └── schema.resolvers.go
├── infra // Infra層
│   ├── cloudSQL.go
│   ├── firestore.go
│   ├── http.go
│   ├── infra.go
│   ├── memory.go
│   ├── pubsub.go
│   └── session.go
├── main.go
├── registry // Registry層
│   └── registry.go
├── start.sh
└── usecase 
├── presenter // Presenter層
│   └── presenter.go
└── service // Service層
    └── service.go