サマリー
- サービス開始から3年経った Next.js + Rails なシステム
- 全ての API を REST から GraphQL にリプレース
- 約9ヶ月かかりました
- 早速フロントエンドの都合でバックエンドにも手を入れるということが減って快適です
という話です。
システム構成の変遷
創業フェーズ
1人目エンジニアとして入社して、何から手を付けようかなーと考えた結果、事業の肝の部分からシステム化していくことにしました。弊サービス https://moneiro.jp/ は資産運用のアドバイスをする事業で、当時はセミナー予約と相談予約という大きく2つの入り口と、ファイナンシャルアドバイザーが予約を確認したり、相談の記録(社内では相談を面談と呼ぶので通称:面談記録)を残すなど、主要な業務を全てスプレッドシートで行っていました。こういうのはエンジニアがいない会社ではあるある(ですよね?)なので、個人的にはそこまで驚きはなく「ではこの辺りからシステム化していきましょうか!」と慣れた感じでスタートしました。
予約部分は外部サービスを使っていたので、その外部サービスから予約データを取り込んだり、ファイナンシャルアドバイザーが業務を行う社内システムから作ることに。という背景だったので「素朴な社内システムなら Rails でサクッと作ってしまおう」と。はじまりはそれぐらいの意思決定でした。
まだ事業が当たるかも分からないシード期のスタートアップの社内システムを、エンジニア1人きりで SPA + GraphQL サーバーみたいな構成では作り始めないですよね?
結果的に作りたかった社内システム(冒頭で説明した主要機能以外にも多くの業務をシステム化しました)は爆速で開発ができ、経営陣も驚いていました。当面はこの素朴な Rails 製システムを事業の成長に合わせて育てていきます。その間にエンジニアは僕を含め3人体制になりました。
はじめての API と技術選定
しばらくすると「エンドユーザー向けのフロントエンドを作ろう」という話になり、手始めにファイナンシャルアドバイザーの空き状況を反映した予約カレンダーから作ることになりました。
予約カレンダーとは、現行デザインですがこういうものです。
フロントエンドを React / Next.js で作ることはすぐに決まり、ファイナンシャルアドバイザーの空き状況をバックエンドで計算して何らかの方法で返す必要があります。
当時は
- 社内システムを進化させていくプライオリティが高め
- エンドユーザー向けの開発は最低限必要なものからやっていこう
という方向性だったので「フロントエンドからあの手この手でデータを取得するような世界はまだ当面(自社においては)先だろう」と考え、GraphQL ではなく REST で API を作ることにしました。
GraphQL 移行直前
それから2年と数ヶ月が経ち、それぞれ増えたものの説明は割愛しますが、GraphQL 移行直前はこんな構成になっていました。組織としてはエンジニア3人の1チームからエンジニア7人の2チーム体制になっていました。
GraphQL への移行を決めたきっかけ
- フロントエンド側の GraphQL + React Hooks の開発体験の良さ
- (場合によっては)バックエンドや REST API を改修せずに、フロントエンドの改修だけで目的が達成できる時がある
(1) は言わずもがなですが、(2) も地味に嬉しかったりします。開発体験だけでなく開発効率も実際にアップしますよね。
GraphQL 移行方針
GraphQL への移行は、以下の方針で進めることにしました。
- 今後追加する API は REST ではなく GraphQL で実装する
- 改修の機会がある既存 API は、その改修のついでに GraphQL 化する
- 改修の機会がない既存 API は、適宜 GraphQL 化する
GraphQL サーバーは、Rails に既に実装されているビジネスロジックを流用して API のインタフェースを GraphQL に移行することだけにフォーカスするために、別言語や別フレームワークで実装するのではなく、素直に graphql-ruby
を採用しました。
はじめに既存のコントローラーをリスト化して移行の進み具合を見える化しました。
各コントローラーの移行順序としては、
- バックエンド側で GraphQL クエリー、ミューテーションを実装する
- フロントエンド側で API の呼び出しを REST から GraphQL に変更する
- ここまでで一旦リリース
- 数日経ったら Datadog でログを確認して、その API の REST へのアクセスが残っていないか確認する
- REST へのアクセスが無くなっていたらバックエンド側でコントローラーと関連コード(
config/routes.rb
の定義なども)を削除する - いっちょあがり
という流れで1つ1つ行いました。
移行期間
先ほどの移行方針は結果的に正解でした。追加実装ははじめから GraphQL で行われ、改修の機会がある既存 API から GraphQL に置き換わっていく流れで、2022年9月から開始して2023年1月頃までにこの条件に該当する API は追加開発・改修ともに止めずに進められ、ほぼほぼ移行できました。
残すはそれらの条件に該当しない「改修の機会がない既存 API」ですが、改修の機会がないと言ってもこの期間中に変更する機会がなかっただけで、ばりばり使われている API ばかりです。これらを2023年2月から移行していき2023年6月5日に完了しました。
ということで、2022年9月から2023年6月までの約9ヶ月で達成しました!
ふりかえり
1つ目の方針は正解だった
1つ目の「今後追加する API は REST ではなく GraphQL で実装する」という方針は、意思決定をした以降 REST API が増えないように抑制できたので正解だったと感じています。この方針にせずに移行期間中も REST API が増えることを許してしまうと、GraphQL 化のスピードが追加開発のスピードを超えない限り、いつまで経っても移行が終わらないという状況になりもっと長期戦になっていたかもしれません。
2つ目の方針は微妙だったかもしれないけど、正解だったかもしれない
2つ目の「改修の機会がある既存 API は、その改修のついでに GraphQL 化する」は正直ちょっと微妙だったかもしれません。改修したい内容というのは早く世に出した方が良いから着手するわけで、それを GraphQL 化とセットにするとリリースまでの期間が単純に伸びてしまいますよね。
なので、改修自体は REST のまま早く済ませてリリースしてしまい、その後 GraphQL 化を追いかけでやるのが良かったかもしれません。
ただし、そうすると改修自体が済んでしまった API は、3つ目の「その他の API は適宜 GraphQL 化しましょう」と同じステータスになってしまいます。この「適宜やりましょう」が曲者で、やはり「適宜」となってしまうと着手するタイミングを見失うことが多くなりがちです。皆さんもご経験がありませんでしょうか?「適宜やっていきましょう」というタスクは中々着手されないという...。
ということで、やや強引にでも「改修のついでに GraphQL 化しましょう」は、ちょっとした強制力となり GraphQL 化を推し進めることになったので、実は正解だったのかもしれません。どうだろう...。
うーん、物事を進めるのって奥が深いですね。
3つ目の方針はやはり苦戦した
2つ目の段階までは全エンジニア総出で取り組めたのでかなり進みが良かったのですが、3つ目の「改修の機会がない既存 API は、適宜 GraphQL 化する」は前述のとおり中々着手されず苦戦しました。やはり「適宜」なタイミングというのはなかなか訪れないもので放置されることが多かったです...。なので、最後は気合いを入れて私が責任をもってガシガシと移行していきました。
ちなみに、この3つ目の段階の実際の移行期間は、2023年2月から2023年6月はじめまでの約4ヶ月間ですが、こればかりに時間を割けられない状況でこの結果だったので、仮に集中できれば1ヶ月で終わらせられるぐらいのボリューム感でした。
この経験からも「適宜」を「適宜」のままではなく、期間や役割をしっかり決めてプロジェクト化するのが良かったのかなぁと考えています。
「適宜」なくそうキャンペーンですね。
さいごに
何はともあれ、念願・悲願だった全 API の REST から GraphQL へのリプレースが完了しました!!
今後も他の技術的負債を返済したり、チームの粒度とコードのオーナーシップが揃うアーキテクチャに移行していったりと、やりたいことはたくさんありますが、一旦キリの良いタイミングなので備忘録として書いてみました。
REST から GraphQL への移行を検討している方や、今まさに移行奮闘中の方の少しでもお役に立てればと思います。
やっていきましょう!それでは!