こんにちは、新規事業開発室に所属するソフトウェアエンジニアの加藤です。私は関西支店でBill Oneという新規サービスの開発に携わっています。
弊社にはSansanのカタチという企業理念があり、働く人々が体現すべきValuesの1つに「意思と意図をもって判断する」があります。ソフトウェアエンジニアとして、意志と意図をもって利用する技術を選定することは当然かもしれませんが、細部までそれを徹底するのは難しいこともあります。本稿では、私たちが使用している技術やライブラリを振り返って、どんな意志と意図があるかを確認していきたいと思います。
などと硬めのことを書きましたが、他所のチームでは普通に使われているライブラリを意外と知らなかったりするので、似たような記事を読みたいなと思って、技術選定を公開してみる次第です。まずはバックエンド編です。
前提
私たちのチームで開発しているBill Oneは今年の5月にローンチしたばかりのサービスで、チームのエンジニアは5名です。0→1をとにかく作り上げるというフェーズは過ぎて、プロダクト・マーケット・フィットを目指している段階です。開発しているアプリケーションはSingle Page Application (SPA) で、エンジニア全員がフロントエンドもバックエンドも両方担当します。
バックエンドには、Backend for Frontend (BFF) と呼ばれる部分もありますが、薄い層なので本稿では対象外とします。
主要な技術選定
まずは、主要な技術選定です。昨年登壇した際にも話したものもありますが、時間が経って変わったところもありますし、改めて紹介します。
クラウドプラットフォーム: Google Cloud Platform (GCP)
クラウドプラットフォームとして、GCPを使っています。採用理由は以下の通りです。
- もともと他部署でも使っていて知見がある
- Google App Engine (GAE) やCloud Runなどのサーバーレスで運用できるPaaSが整っており、新規事業に向いている
- G Suite連携でアカウント管理が容易
GCPは若干とっつきづらいところもありますが、GAEやCloud Runで普通のWebアプリケーションをサーバーレスで運用できるのはとても強力で気に入っています。
利用言語: Kotlin
サーバーサイドではKotlinを利用しています。Kotlinの採用理由は次の通りです。
- 静的型付け言語で保守しやすい
- Google App EngineのStandard 第2世代ランタイムで動作する *1
- 初期メンバーに得意な人がいた
- ある程度の生産性を期待できるモダンな言語
- 関西での新しいメンバーの採用に困らなさそう
パターンマッチが欲しいとか、標準ライブラリに (Resultではなく) Either的な奴が欲しいとか思うこともありますが、シンプルな記述で不具合の少ないコードを書くことができ、概ね満足しています。
Webアプリケーションフレームワーク: Ktor
WebアプリケーションフレームワークにはKtorを使用しています。Ktorの採用理由は次の通りです。
- Kotlinの言語特性を生かせる
- 軽量だが必要十分な機能がある
- JetBrainsによる開発で継続性の面で安心感がある
Google App EngineやCloud Runなどのアクセスがない時に0までスケールインするクラウド実行環境を使う上では、アプリケーションの起動速度がリクエストのレイテンシに直結します。採用時にSpring Bootも検討しましたが、Ktorの方がGAE上での起動が早く、そこが決め手となりました。
必要十分と書きましたが、軽量なフレームワークでまだ発展途上なところはあり、例えば 認証 (Authentication) の仕組みはありますが、認可 (Authorization) の仕組みは用意されていないので、自分で実装する必要があります。
データベース: PostgreSQL
スケーラビリティよりもデータの整合性が重要なアプリケーションなので、リレーショナルデータベースを採用しています。Cloud SQLで利用できて、メンバーが使い慣れていたPostgreSQLを採用しています。私たちのアプリケーションでは主キーには基本的にUUIDを使用しており、UUID型のサポートがあるのもメリットです。
DB接続ライブラリ: JDBI
私たちのチームでは、O/Rマッパーは使わずに生のSQLを書いています。JDBIを使って、DBへの接続、SQLへのパラメーターのバインディングおよび実行結果のオブジェクトへのマッピングを行っています。
もともとSql2oを使用していましたが、一部の型で期待した通りの変換が行われないことがわかりました。改めて比較検討したところ、JDBIのPostgreSQL周りのサポートが良好だったため採用しました。
使っていない技術
よく使われているけど使っていない技術についても触れておきます。ここも意志と意図が出やすいところだと思います。
O/Rマッパー
上述のように、O/Rマッパーは使わずに生のSQLを書いています。元々のチームメンバーの志向として、SQLをそのまま書く方が見通しが良くて好みというメンバーが多数派だったためです。
O/Rマッパーに詳しいメンバーがいない状況で、新しくO/Rマッパーのブラックボックスの中を学ぶよりは、SQLを書いた方が開発スピードが上がるし、将来的にパフォーマンス問題に遭遇する可能性を減らせるだろうという判断です。
DIコンテナ
DIコンテナも使っていません。複雑性を導入する割に、得られるメリットに懐疑的だったためです。一般にDIコンテナのメリットとして、次の2点が挙げられます。
- 実装を差し替えやすい
- テストがしやすくなる
ですが、実際に実装を差し替える機会はほとんどありません。利用する技術を変えたら、もともと考慮していたインターフェイスでは扱えないという可能性もあります。
テストに関しても、依存オブジェクトを差し替えた上でのテストで本当に実装が間違っていないことを保証できるか確信を持てません。それよりはHTTPリクエスト〜DB〜HTTPレスポンスまでを通貫するE2Eテストを丁寧に実施して、どうしても差し替えが必要なところだけをモックで差し替える方が不具合を減らせるだろうと考えました。
これらの考えが正しいか間違っているかは時間が経たないとわかりませんが、間違っていたとしてもコードを捨てて書き直せるように小さな状態を保っていきたいと思います。
その他のライブラリ
最後に、これまでに紹介できていないその他のライブラリを紹介します。正直なところJavaやKotlinのエコシステムを熟知しているとまではいかないので、強い意志があるというよりは、ある程度デファクトスタンダードと思われるものを採用しています。
- アプリケーションコード
- Jackson: JSONのシリアライズ・デシリアライズ
- SLF4J + Logback: ロギング
- HikariCP: コネクションプール
- postgres-socket-factory: Cloud SQLへの接続
- Google Cloud Storage Client for Java: Cloud Storageのクライアント
- Google Cloud Tasks Client for Java: Cloud Tasksのクライアント
- Apache Commons CSV: CSVの作成・パース
- icu4j: 文字コードの自動判別
- Fuel: 汎用的なHTTPクライアント
- FreeMarker: テンプレートエンジン(メール文面の作成に使用)
- Apache PDFBox: PDFファイルの処理
- Zip4j: ZIPファイルの処理
- テストコード
- JUnit 4: テストフレームワーク
- AssertJ: テストのアサーション
- DbSetup: テストフィクスチャーのDBへのロード
- Mockk: テストでのモック
- json-fuzzy-match: 動的な値を含むJSON文字列のアサーション
最後に
本稿では、私たちのチームでの技術選定を紹介しました。新規事業開発室では開発中のプロダクトがいくつかあり、隣のチームではまた違った技術選定が行われています。
技術選定に絶対の正解はなく、時間の制約がある中でより良い選択肢を選べるよう、常に意志と意図をもって考え続けていく必要があります。本稿が読者の皆さんの技術選定の参考になれば幸いです。
*1:現在はCloud Runに移行済み