DeNA.go #2でアーキテクチャとテストについて考える
チーム開発で、Goに本格的に取り組むことになって少し経ったところなのですが、最近Goで気になっているのはアーキテクチャまわりとテストだったので、その点に注目して参加してきた。
## 1 新ゲームサーバ基盤TakashoでのGo言語活用事例の紹介
- 自社ネイティブアプリ(ゲーム)の共通サーバ基盤のお話
- ひとつのサーバで共通の機能(ログイン、プレイヤーデータ等)を一元管理するための基盤
- 共通故の制約が多く、ゲーム毎の拡張自由度が小さいのが課題
共通基盤あるある。しかし、ゲーム業界はタイトル毎の要求項目が細かく違いそうなモノだけど、かなり難しいのではなかろうか…
と思ったが、いわゆるガチャも共有の仕組みの一つのよう。収益の本流も共通なら、なるほど必要だろうなぁ。。
- 新サーバ基盤
- Webサーバフレームワークと、クライアントSDKとあわせて提供
- サーバはGo、クライアントはC#
- 1サーバ1クライアント構成
- サーバはGCP上にterraformで構築
- パッケージ管理ツールはdep(go modulesに移行中)
- サーバをGoにした理由は、学習コスト対効果が良いから。そして安定性があること。
具体的に他のどの言語やフレームワークとの比較で学習コストが低かったのかを聞きそびれてしまった。
構成もGCP特化でストレージはCloud Spannerとのことなので、このあたりはゲーム用基盤として最適化されていて便利そう。
ゲームユーザ向けならスケーラビリティも非常に重要であるし、データ量もさぞ膨大だろう、、
開発者はgo getして利用するあたりは、package単位で利用するGoの利便性が際立っているように思える。
このあたり、package設計は重要なポイントだろうと思う。
- コードの自動生成
- GAEでgRPCが使えないから辛い→独自実装しよう
- protobuf
- 拡張protocコマンドでサーバサイド、クライアントサイドのコードを自動生成
- go templateでコードテンプレート
- テストコード自体の自動生成
- できるかぎり自動化をがんばる
ProtocolBufferはなかなか良いとの推しを頂く。
インターフェースだけでなく、ドキュメント生成もできるらしい。なにそれ(無知)
gRPCの代替として使えるくらいのスキーマ言語らしいので十分だろうと思うが、RESTでも使えるだろうか?
ちなみにコードの自動生成の観点では、go generateを使う方向で検討していたが、今回の勉強会ではgenerateの話は一切無かった。
コード量が多いと言われるGoにあって、できる限りの自動化はやりたいところ。
## 2 次世代タクシー配車サービス「MOV」におけるテスト事例紹介
- テスト方針
- テスト全体に関わるfixtureを使わない
- マスタデータ以外をDBにロードしない
- 並列テストのために、bxcodec/faker v3でデータ生成
- 構造体にgoタグをつけてデータを自動生成する
- 本番用のコードにgoタグつけるのが嫌なので、テスト用に同じ構造体を自動生成してる
前者は順当な話。テストの実施に必要なデータは冪等性を担保すべきだ。
並列テストにおけるfakerはなかなか便利そう。テストデータを用意する上で省エネが図れるなら試す価値はあるかも。
本番用のコードにgoタグつけるのが嫌
このあたりは非常に意見が割れそうだが、そのテストだけのために同一構造体自体をテスト用に自動生成するのは気合入った話だと思う。
## 3 DeSCヘルスケアにおけるGo 活用事例紹介 #DeNAgo
- レイヤードアーキテクチャ + DI
- 依存性逆転の原則を守る
- ライフサイクルをレイヤード分離
- gomockを使って、テスト中はmockをDIする。
- ただでさえコード量、テスト対象が増えるため各レイヤーでは最小限
- DBデータ取得、保存はinfrastructure層
- gotestsコマンドでテストの雛形を生成
- エンベロープ暗号
- 会社のセキュリティポリシーとしてDBの暗号化+カラムの暗号化
アーキテクチャのお話で、レイヤードアーキテクチャを採用しているとのこと。
ディレクトリは割と素直な名付けになっている。噂によるとメルカリさんもレイヤードアーキテクチャらしい。
ぼくのチームはクリーンアーキテクチャで進めているため、読み替えができれば大きな違いは無し。
導入メリットは、やはり変更時の影響分離が大きいところだそう。
エンベロープ暗号は聞いたことが無かったのでこれは別途調べる。
## そのほか
いろんな方面から耳にした内容。サンプル数の少ない情報として、あくまでメモ程度。
- Infrastructure層のテストは実際のFirestoreを叩きに行っているとのお話があり
ローカルシミュレータの挙動がちょっと…ということのようだが、テストを回す毎にお金がかかるのとパフォーマンス効率の面では厳しい部分がある。
ただし、本番の環境を叩きに行くテストのメリットはあり、悩ましいところのようだ。
Firestoreは複雑なクエリを書くことができないそうなので、テストケース自体が簡素なのだろうか?
- ネイティブアプリはSwift, Kotlinでそれぞれ作ることが多く、Flutterはまだちょっと…
そのうちFlutter勢力も増すかもしれないが、直近のGoogle I/OでもKotlin firstと言われている段階なので時期尚早なのかもしれない。
- エディタはみんな好きにやってる
最近になって、VSCodeはコード補完(LSP)が良い感じに向上してきたようで増えてきた模様。
JetBrainsは独自の補完を実装しており、それが爆速(らしい)。
- Goでテストの可読性を上げるためには
テーブルドリブンテストはおすすめ。
データや期待値は.golden拡張子で定義することができる。
- やはり、RDBMSのいい感じのデファクトORMはなさそう
が、黒魔術感がなく薄めのORMとして構造体の受け渡しができるsqlxは良さそう。
## まとめ
- もともとCやRubyを書いていた人がGoも触れているというところから、参入コストは低い。君でもやれる。
- Goはそもそもコード量は多い。そのため、チーム開発ではコードの自動生成をどうするか真面目に考える必要がある。猫も杓子もテストもドキュメントも自動化
- プロダクトやチーム規模によるが、サービスの改善を行うためにもドメインの分離をするためにもアーキテクチャは大事。コード量増加は辛く折れそうになるが自動化と気合で乗り切る。
- Goのテストはテーブルドリブンの導入やツール類を駆使することで可読性は随分違う。
- DeNAさんの勉強会は選べるお弁当、選べるドリンクが豊富であたまが下がる思い。ごちそうさまでした。