2020-05-13 RailsとJSON:APIによるマイクロサービス構築事例
- 7. 7
● ビジネス観点
○ プロダクトが多い
○ プロダクトがどんどん増える
○ 各プロダクトがそれぞれ独自のプランラインナップを持つ
■ ベーシックプラン、エンタープライズプランなど
○ ラインナップもどんどん変わる
○ 継続課金と都度課金が混在
■ 継続課金 = 年払いや月払いのサブスクリプション契約
■ 都度課金 = 物販(印鑑)や手数料など
● 技術観点
○ プロダクトはだいたいRailsで開発
○ 価格表・契約管理・請求・決済にはサブスクリプション管理 SaaS の Zuora を利用
freeeの課金基盤の特徴
- 13. 13
Rails6の採用理由
● 現行の共通課金処理のコードを再利用したかった
○ 時間的制約も厳しかった
● 当時の課金基盤チームが最も習熟していたのがRailsだった
● 開発開始当初、まだRails6は正式リリースされていなかったが、どうせなら新しいバー
ジョンのほうがよいのと、新課金基盤稼働(2019年10月)までには正式リリースされる
見通しだった
EKSの採用理由
○ 特にない
■ freee の新たな標準インフラ構成(Docker + k8s + EKS)に従ったらそうなった
○ なお、EKS を実戦投入した最初の社内事例になった
- 17. 17
単一オブジェクトの取得
# リクエスト
GET /api/objects/licenses/315
# レスポンス
{
"data": {
"id": "315",
"type": "licenses",
"attributes": {
"company_id": 10,
"source": "zuora",
...
"quantity": 1,
"start_date": "2020-03-31",
"end_date": "2021-03-30",
"created_at": "2020-04-29T15:26:44.502+09:00",
"updated_at": "2020-04-29T15:26:44.502+09:00"
}
}
}
リソース名
識別子
その他の属性
キー “data” の値はオブジェクト
オブジェクト ≒ レコード
- 19. 19
JSON:APIの採用理由
● Rails なので HTTP + JSON かな、と
○ いちおうgRPCも検討したが、Railsでの構築ノウハウがなかったのでやめた
● 独自JSONフォーマットを設計する意義を感じなかった
○ 柔軟性、整合性、拡張性に欠ける劣化版になる可能性が高い
● 既存ライブラリの活用による生産性向上
- 20. 20
freee 新課金基盤におけるJSON:API実装
● サーバー側(新課金基盤)
○ ActiveModel::Serializers (AMS) と自前実装を併用
■ AMSは名前の通りActiveModelオブジェクトをシリアライズするためのgemで、JSON:APIサーバー
としての汎用機能はほとんどない(リクエストをデシリアライズする試験実装はちょっとある)
○ リクエストのパースと実行 → 自前実装
○ レスポンス生成
■ リソース実体がActiveRecord → クエリー結果をAMSでシリアライズ
■ リソース実体がZuoraからのJSONレスポンス → 自前でJSON:API形式に整形
なぜ自前実装?
● 当時は良さげなRails向けJSON:APIサーバーgemが見つからなかった
● 一方でAMSのシリアライズ機能は、それだけでも使いたいと思うほどパワフルだった
● そもそもJSON:APIプロトコル自体は比較的シンプルで、実装難易度は高くなかった
- 21. 21
freee 新課金基盤におけるJSON:API実装
● クライアント側(会計freee、人事労務freeeなど)
○ JsonApiClient をラップしたgemを配布
■ ActiveRecordライクなインターフェイス
■ 少ないコーディング量
■ 型キャストなどのカスタマイズが可能
■ 同種の他のgemに比べて Star の数が多かった
class License < JsonApiClient::Resource
self.site = "http://localhost:3008/api/objects"
end
license = License.where(company_id: 10).first
# 以下のHTTPリクエストが発行され、レスポンスがActiveModelオブジェクトに変換される
# GET http://localhost:3008/api/objects/licenses?filter%5Bcompany_id%5D=10
license.note = 'TEST'
license.save
# 以下のHTTPリクエストが発行される(ペイロードは省略)
# PATCH http://localhost:3008/api/objects/licenses/315
- 23. 23
良かったこと
● ビジネス観点
○ 新規プロダクトの課金実装が非常にスムーズに
● 技術観点
○ Railsバージョンは新しいものに限る
■ 特にメジャーバージョンアップはそれなりに手間
○ JSONフォーマットで悩む時間を節約できた
○ RESTに忠実な一貫性のあるAPI設計となった
■ JsonApiClientの活用がある種の制約になった
○ 新規APIエンドポイント作成時のクライアント側の対応が非常に楽
■ JsonApiClientのおかげ
- 24. 24
苦労したこと
● Rails6がなかなか正式リリースされなかった
○ 予定では4月末だったが、実際にリリースされたのは8/15だった
○ いちおうRails5に戻してリリースもできるよう、Rails6の新機能は使わずにおいた
● チームにEKS / コンテナ運用のノウハウが欠けていた
○ 最近は解消されつつある
● JsonApiClientと他のgemとの、依存Faradayバージョンのコンフリクト
○ 最新のJsonApiClientは比較的最近のFaradayバージョンに依存しているが、会計freeeで利用してい
る別のgem(古め)が、古いバージョンのFaradayにしか対応していなかった
■ JsonApiClientは別に悪くない
○ 古いJsonApiClientバージョンに独自パッチを当てて対応