Upgrade to Pro — share decks privately, control downloads, hide ads and more …

会計 freee バックエンドの今後 / freee backend api

Mihyaeru
February 09, 2021

会計 freee バックエンドの今後 / freee backend api

【Engineering Team Presentation】各社の事業を支えるアーキテクチャ
https://sansan.connpass.com/event/200589/
での発表資料です。

Mihyaeru

February 09, 2021
Tweet

More Decks by Mihyaeru

Other Decks in Programming

Transcript

  1. スモールビジネス向けに統合型クラウドERPを提供 請求書 | 経費精算 | 決算書 | 予実管理 | 内部統制


    統合型クラウド(1)会計ソフト
 統合型クラウド人事労務ソフト 2013年3月~ 日本のクラウド市場 シェアNo.1 (2) 2014年10月~ 日本のクラウド市場 シェアNo.1 (3) 勤怠管理 | 入退社管理 | 給与計算 | 年末調整
 マイナンバー管理
 その他サービス
 会社設立 開業 税務申告 マイナンバー管理 クレジットカード フリーカード
 4 プロジェクト 管理 注: 1. クラウドサービス:ソフトウェアやハードウェアを所有することなく、ユーザーがインターネットを経由してITシステムにアクセスを行えるサービス 2. 株式会社BCN「クラウド会計ソフトを導入している従業員数300名未満の企業又は個人事業主へのWeb調査(2017年9月実施、2017年10月公表))」(N=418) 3. クラウド給与計算ソフトの市場シェア:株式会社MM総研「日本におけるクラウド給与計算ソフトの利用状況調査に関するWeb調査(2016年3月実施)」(N=4,168)
  2. • バックエンドは Rails
 ◦ 巨大なモノリス+いくつかのマイクロサービス
 ◦ 人事労務 freee など、他の社内プロダクトとの連携あり
 ◦

    銀行などの明細を取得する部分も含んでいる
 ▪ これはサービス切り出しが進んでいる
 ◦ Service, Presenter, Form など、よくありそうなのが app 以下にある
 ◦ lib 以下がカオス
 • フロントエンドは React
 5 会計 freee の基本的な構成

  3. 6 会計 freee の基本的な構成
 主に今回注目している部分について
 会計 freee
 Rails App
 Web


    フロント
 Private API
 Controller
 Service
 モバイル
 アプリ
 3rd Party
 Client
 Public API
 Controller
 Admin
 Controller
 Model
 銀行等
 人事労務 freee
 Rails App
 マイクロサービス

  4. 7 会計 freee の基本的な構成
 ある1つの機能について、各チームの担当領域
 会計 freee
 Rails App
 Web


    フロント
 Private API
 Controller
 Service
 モバイル
 アプリ
 3rd Party
 Client
 Public API
 Controller
 Admin
 Controller
 Model
 銀行等
 人事労務 freee
 Rails App
 マイクロサービス
 会計チーム
 モバイルチーム
 API チーム
 いくつかの チーム

  5. 8 会計 freee の開発に関する数字
 テーブル数
 700くらい
 Ruby ファイル数
 10000くらい
 JavaScript

    ファイル数
 5000くらい
 routes
 4000くらい
 1ヶ月のコミット数
 2000くらい
 1ヶ月のプルリクエスト数
 1000くらい

  6. • 影響範囲が読めなすぎる
 • リファクタリングが怖い
 • 同じく Web API の変更が難しい・変更リスクが高い
 •

    Web フロントエンド以外の開発が後回しになる
 • etc...
 9 大きさとのコンボで辛くなる問題点

  7. • 2年ほど前に会計 freee の一部機能をマイクロサービスに切り出した
 ◦ これは freee Tech Night にて話した


    ◦ 書き込み系は完了したが読み込み系が少し残り、DB 分離が完了していない
 ▪ 別プロダクトからも使いたかった要求は満たせている
 ▪ 移行に見積もりより多くの時間をかけてしまったため、他タスクとの兼ね合いで 優先度を上げられていない
 • 機能を分離する目的だけでマイクロサービス化はしないほうが良い
 ◦ そもそも機能の分離をしないとマイクロサービス化できない
 12 つまりマイクロサービス?

  8. • 分離したい機能をアプリケーション内でモジュールとして分離する
 ◦ たとえば Rails なら Rails Engine として分離
 ◦

    たとえばモジュールのインポートが明示的な環境ならモジュールで分離
 • マイクロサービスでとくに頭が痛くなるトランザクションをどうするかという問題を後回 しにできる
 • 「Modular Monolith」で検索すると Shopify の例がよく出てくる
 13 モジュラモノリス

  9. 14 モジュラモノリス
 Rails Engine を使った場合の単純な例
 Rails App
 機能 A
 Rails

    Engine
 Model
 Controller
 機能 B
 Rails Engine
 Model
 Controller

  10. • Backend API というものを新しく定義して導入する
 • Controller などは Backend API を利用する立場と定義する


    • Backend API の内側をモジュラモノリスにおけるモジュールと定義する
 • この単位でモジュラモノリス化を進めていく
 15 会計 freee ではどうするか
 会計 freee
 Rails App
 Module
 Model
 Service
 Controller
 API

  11. • Backend 内部の API の意味(Web API とは別物)
 • Service のインタフェースを明示的にしたもの


    • 現時点では Protobuf を採用
 ◦ マイクロサービス化したときにそのまま gRPC に流用可能
 ◦ API に ActiveRecord を登場させないという意思
 16 Backend API

  12. • ある機能に属するテーブルに対応する ActiveRecord Model は、その機能の Backend API を実装する Service 内でだけ使用可能


    • 機能をまたいで ActiveRecord Model の関連を作らない
 • ある機能Aの Backend Service 内部から別の機能Bに属するテーブルを読み書きす る場合は、Bの Backend Service を経由する
 • (超汎用的なテーブルなど例外はある)
 17 Backend API と共に導入する制約

  13. 18 Backend API と共に導入する制約
 ある機能に属するテーブルに対応する ActiveRecord Model は、
 その機能の Backend

    API を実装する Service 内でだけ使用可能
 会計 freee
 Rails App
 機能 A
 Model A
 Service A
 機能 B
 Service B
 これもダメ
 Controller
 これはダメ

  14. 19 Backend API と共に導入する制約
 機能をまたいで ActiveRecord Model の関連を作らない
 会計 freee


    Rails App
 機能 B
 Service B
 機能 A
 Model A1
 Service A
 Model A2
 Model A3
 Model B1
 Model B2
 これはダメ

  15. 20 Backend API と共に導入する制約
 ある機能Aの Backend Service 内部から別の機能Bに属するテーブルを読み書きする場合 は、Bの Backend

    Service を経由する
 会計 freee
 Rails App
 機能 A
 Model A1
 Service A
 Model A2
 Model A3
 機能 B
 Service B
 Model B1
 Model B2
 OK
 API B

  16. • 機能と ActiveRecord Model の対応関係を設定ファイルとして用意
 
 • DB 操作時に、現在のコンテキストから操作して良い Model

    なのかを判別
 • development/test 環境でエラーにすることで強制力を持たせる
 
 • 許容される関連かどうかは Model の関連を辿ったものと設定ファイルを比較すること で検知し、CI で常にチェックする
 21 どうやって制約をつけるか

  17. • Private API を Web フロントエンドとモバイルアプリ両方から利用していたのをやめる
 • モバイルアプリ向けに Mobile API

    を新設する
 • 各チームが Controller のオーナーになり、他チームに影響を与えずに Web API を変 更していけるようにするため
 22 Backend API と一緒に行う変更

  18. 23 Mobile API を新設
 会計 freee
 Rails App
 機能 A


    Model A1
 Service A
 Model A2
 Model A3
 Web
 フロント
 モバイル
 アプリ
 3rd Party
 Client
 Private API
 Controller
 Public API
 Controller
 Admin
 Controller
 API A

  19. 24 Mobile API を新設
 会計 freee
 Rails App
 機能 A


    Model A1
 Service A
 Model A2
 Model A3
 Web
 フロント
 モバイル
 アプリ
 3rd Party
 Client
 Private API
 Controller
 Public API
 Controller
 Admin
 Controller
 Mobile API
 Controller
 API A

  20. 25 Mobile API を新設
 会計チーム
 モバイルチーム
 会計 freee
 Rails App


    いくつかの チーム
 機能 A
 Model A1
 Service A
 Model A2
 Model A3
 Web
 フロント
 モバイル
 アプリ
 3rd Party
 Client
 Private API
 Controller
 Public API
 Controller
 API チーム
 Admin
 Controller
 Mobile API
 Controller
 API A

  21. 26 Backend API 導入後の図
 会計 freee
 Rails App
 機能 B


    Service B
 機能 A
 Model A1
 Service A
 Model A2
 Model A3
 Model B1
 Model B2
 Web
 フロント
 モバイル
 アプリ
 3rd Party
 Client
 Private API
 Controller
 Public API
 Controller
 Admin
 Controller
 Mobile API
 Controller
 API A
 API B
 Controller が Service A に直接依存しないように描かれているが、API A の存在を強調するためにそう描いているだけで実際には Service A に 直接依存する。A から B についても同様。実際の構成要素のうち説明に関係のないものは省いている。

  22. • Service 実装より前にインタフェースを作ることになるため、Service 実装中でもモバ イルアプリや Public API を並行で作れるようになる
 ◦ これを行いたいのが発端だった


    • 機能実装の影響範囲がはっきりする
 • Web API の変更が各チーム内で完結する
 27 Backend API 導入でどう良くなるか

  23. • ActiveRecord が制限され、Rails Way から大幅に外れる
 • Backend Service の外側では ActiveRecord

    を前提としたライブラリが使えなくなる
 ◦ 例えばシリアライズのための ActiveModel::Serializer
 ◦ 普通なら Model で関連を定義して preload を書いて終わりなデータ取得が面倒に なる
 • つまり、レールを自分たちで考え、作る必要がある
 28 Backend API 導入のデメリット

  24. • JSON シリアライズ時に ActiveRecord と Serializer は何を司っているのか
 
 • Serializer


    ◦ Model のシリアライズ後の構造を定義
 ◦ Model をその構造に変換
 • ActiveRecord
 ◦ Model のデータを取得
 ◦ 他の Model との関連を定義
 29 脱 Rails Way の例
 Serializer を使わないならどうするか

  25. • JSON シリアライズ時に ActiveRecord と Serializer は何を司っているのか
 
 • Serializer


    ◦ Model のシリアライズ後の構造を定義
 ◦ Model をその構造に変換
 • ActiveRecord
 ◦ Model のデータを取得
 ◦ 他の Model との関連を定義
 30 脱 Rails Way の例
 Serializer を使わないならどうするか
 ViewModel
 ViewModelBuilder
 Fetcher

  26. • ViewModel
 ◦ 構造を定義するのみ
 • ViewModelBuilder
 ◦ Model を ViewModel

    に変換する
 ◦ 変換時に必要になる関連先 Model とそれを取得する Fetcher を定義する
 ▪ Builder は定義するだけで、具体的な取得方法は知る必要がない
 ▪ ActiveRecord は使わないけど preload のような動きを実現
 • Fetcher
 ◦ Backend Service を用いて関連先 Model を取得する
 31 脱 Rails Way の例
 コードレベルで解説する時間枠がないので、詳しくはいずれ freee Developers Blog にて紹介 する

  27. • ざっくりした PoC の実装を終えた
 • 1つの機能を対象にして実際に Backend API を導入中
 ◦

    PoC では見えなかった細かい問題と戦いながら仕組み化中
 32 現在の状況

  28. • 「開発者が見るべきスコープを狭める」という基本方針
 ◦ 開発者とコードが増えても安全を確保しつつ開発速度を維持するため
 • モジュラモノリスを目指す
 • そのために Backend API

    を定義
 • Rails Way から外れる部分は用途に応じて小さなレールを作る
 ◦ 用途が限定されたレールは案外小さくシンプルに作れる
 33 まとめ