freee 株式会社

【シューマイ】Tech Lead Engineerから最新技術を学べ!Rails編

Rails と JSON:API によるマイクロサービス構築事例

2020.05.13
freeeで6年目のRailsエンジニア。

人事労務freee、マイナンバー管理freee等の開発に携わった後、現在は全プ
ロダクト共通の「課金基盤」開発チームのエンジニアリングマネージャー。



テクニカルコンサルタントとして培った的確なビジネス要件定義力に加え、本
質を突くデータモデリングスキルと独創的なソリューション設計力が強み。



日本に10数人しか合格者がいないSalesforce資格の最高峰、Salesforce認定
テクニカルアーキテクト資格も持つ。



freee 2代目巨匠。



好きなものはビールと巨人とSQL。



各種サービスの id はだいたい yebihara。

EBIHARA, Yuichiro
海老原 雄一郎

freee株式会社

2
3
Railsでの新規マイクロサービス構築事例

今日お話しすること

01 背景

4
Section
5
freeeの「課金基盤」チームとは

freeeが提供する各種プロダクトの

● 契約管理

● 料金計算

● 電子決済

のためのシステムを開発・運用するチーム。

6
freeeは何の会社?

(主に)

スモールビジネス向けのSaaSプロバイダー

● 会計freee(法人向け / 個人向け)

● 人事労務freee

● 会社設立freee

● 開業freee

● 申告freee

● プロジェクト管理freee

● その他、多くの関連サービス

○ freeeカード、請求書ファイナンス、オファー型融資、

創業融資freee、マイナンバー管理freee、freee for Salesforce などなど

7
● ビジネス観点

○ プロダクトが多い

○ プロダクトがどんどん増える

○ 各プロダクトがそれぞれ独自のプランラインナップを持つ

■ ベーシックプラン、エンタープライズプランなど

○ ラインナップもどんどん変わる

○ 継続課金と都度課金が混在

■ 継続課金 = 年払いや月払いのサブスクリプション契約

■ 都度課金 = 物販(印鑑)や手数料など

● 技術観点

○ プロダクトはだいたいRailsで開発

○ 価格表・契約管理・請求・決済にはサブスクリプション管理 SaaS の Zuora を利用

freeeの課金基盤の特徴

8
課金基盤チームのミッション

顧客とfreee社内ユーザー、双方にとっての課金UX向
上を通じてfreeeの価値が顧客に届きやすくすると同
時に、freeeのビジネスのアジリティ・収益性・生産性の
向上に寄与する。

02 システム構成

9
Section
10
システム構成(before)

会計
freee
共通課金処理(gem)
課金UI
ユーザー
Zuoraデータ
キャッシュ
人事労務
freee
共通課金処理(gem)
課金UI
Zuoraデータ
キャッシュ
● ポイント

○ 課金UIはプロダクト毎に個別実装

○ 複数プロダクト共通の課金処理をgem化

○ Zuoraの管理するデータのうち、各プロダクトが
必要なものをそれぞれがローカルDBにキャッ
シュ

● 課題

○ 似たような課金UIの重複実装

○ 共通課金処理を修正したときの gem アップ
デートが面倒

○ 他プロダクトの契約情報の参照

○ ユーザーにとって「全ての契約情報が集約され
ている場所」がない

11
システム構成(after)

会計freee
gem
ユーザー
人事労務freee
Zuoraデータ
キャッシュ
新課金基盤
課金UI
API
API
gem
● 契約管理・課金関連機能を「新課金基
盤」として独立サービス化

○ 課金UIの汎用性を高めて一元化

○ 課金処理・ZuoraとのI/Oも一元化

○ Zuoraデータのキャッシュも一元化

○ 各プロダクトには簡素なクライアント gem
を配る

12
システム構成(after)

会計freee
gem
ユーザー
人事労務freee
Zuoraデータ
キャッシュ
新課金基盤
課金UI
API
API
gem
● 契約管理・課金関連機能を「新課金基
盤」として独立サービス化

○ 課金UIの汎用性を高めて一元化

○ 課金処理・ZuoraとのI/Oも一元化

○ Zuoraデータのキャッシュも一元化

○ 各プロダクトには簡素なクライアント gem
を配る

ここがJSON:API
ここがRails6 + EKS
13
Rails6の採用理由

● 現行の共通課金処理のコードを再利用したかった

○ 時間的制約も厳しかった

● 当時の課金基盤チームが最も習熟していたのがRailsだった

● 開発開始当初、まだRails6は正式リリースされていなかったが、どうせなら新しいバー
ジョンのほうがよいのと、新課金基盤稼働(2019年10月)までには正式リリースされる
見通しだった

EKSの採用理由

○ 特にない

■ freee の新たな標準インフラ構成(Docker + k8s + EKS)に従ったらそうなった

○ なお、EKS を実戦投入した最初の社内事例になった

03 JSON:APIとは

14
Section
15
JSON:APIとは

● HTTPとJSONを利用してデータ操作を行うための標準仕様

○ 公式サイト https://jsonapi.org/

● 様々なプログラミング言語・フレームワーク向けのサーバー・クライアント実装が存在
(一覧)

○ Ruby (Rails) 向け

■ ActiveModel::Serializers

■ Netflix/fast_jsonapi など

16
JSON:APIがサポートする機能

● リソースのCRUD操作

○ リソースオブジェクトの検索(GET)

■ フィルター(条件指定)

■ ソート

■ リレーションシップ(関連レコードのネスト)

■ ページネーション

○ リソースオブジェクトの作成(POST)

○ リソースオブジェクトの更新(PATCH)

○ リソースオブジェクトの削除(DELETE)

● 操作結果の通知

○ HTTPステータス

○ エラー構造体

○ メタデータ(任意の補足情報)

操作対象データが ActiveRecord なら、
リソース = テーブル
と考えて差し支えない。
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” の値はオブジェクト
オブジェクト ≒ レコード

18
複数オブジェクトの取得

# リクエスト
GET /api/objects/licenses?filter%5Bcompany_id%5D=10
# レスポンス
{
"data": [
{
"id": "315",
"type": "licenses",
"attributes": {
"company_id": 10,
...
}
},
{
"id": "317",
"type": "licenses",
"attributes": {
"company_id": 10,
...
}
},
...
]
}
キー “data” の値はオブジェクトの配列
フィルター デコードすると filter[company_id]=10

オブジェクト

オブジェクト

19
JSON:APIの採用理由

● Rails なので HTTP + JSON かな、と

○ いちおうgRPCも検討したが、Railsでの構築ノウハウがなかったのでやめた

● 独自JSONフォーマットを設計する意義を感じなかった

○ 柔軟性、整合性、拡張性に欠ける劣化版になる可能性が高い

● 既存ライブラリの活用による生産性向上

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
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
04 現状と今後

22
Section
23
良かったこと

● ビジネス観点

○ 新規プロダクトの課金実装が非常にスムーズに

● 技術観点

○ Railsバージョンは新しいものに限る

■ 特にメジャーバージョンアップはそれなりに手間

○ JSONフォーマットで悩む時間を節約できた

○ RESTに忠実な一貫性のあるAPI設計となった

■ JsonApiClientの活用がある種の制約になった

○ 新規APIエンドポイント作成時のクライアント側の対応が非常に楽

■ JsonApiClientのおかげ

24
苦労したこと

● Rails6がなかなか正式リリースされなかった

○ 予定では4月末だったが、実際にリリースされたのは8/15だった

○ いちおうRails5に戻してリリースもできるよう、Rails6の新機能は使わずにおいた

● チームにEKS / コンテナ運用のノウハウが欠けていた

○ 最近は解消されつつある

● JsonApiClientと他のgemとの、依存Faradayバージョンのコンフリクト

○ 最新のJsonApiClientは比較的最近のFaradayバージョンに依存しているが、会計freeeで利用してい
る別のgem(古め)が、古いバージョンのFaradayにしか対応していなかった

■ JsonApiClientは別に悪くない

○ 古いJsonApiClientバージョンに独自パッチを当てて対応

25
今後の課題

● 実はまだ会計freeeの新課金基盤移行が済んでいない

● それが終わったらプロダクト全体の課金UXの向上に本格着手



新たな価値を素早く提供

→ それに見合う対価をお客様から手間なくいただく

→ 新たな価値の創造に投資

→ 最初に戻る

スモールビジネスを、

世界の主役に。

2020-05-13 RailsとJSON:APIによるマイクロサービス構築事例