「奥野さんと社員のリファクタリング部屋」は、リファクタリングに励むトレタの社員と技術顧問の奥野さん ( @okunokentaro ) の間で実際に行われた会話を切り取った開発現場実録コンテンツです。
技術顧問: 奥野さん 三度の飯よりリファクタリングが好き
今回の質問者: 武市さん トレタ在籍2年。沖縄在住のフロントエンジニア
PR
こちらに登場している2人が登壇するイベント・TORETA TECH UDATEが2024/08/28に開催予定! リファクタリングやリアーキテクトなど、サービスを提供させながらプロダクトの新陳代謝をいかにして行なってきたかを開発現場の実例と合わせて紹介します! toreta.connpass.com
今回の質問💬
現在BFFの実装で、各ページごとは基本的に一つのAPIにリクエストをしています。ただ、このAPI使い回されている場合もあります。
例えば、「商品取得API」というAPIを作成して、それは商品詳細を表示するために使われているページで使われているAPIです。 一方で、商品を編集するページからも「商品取得API」を呼び出しています。 これ自体は問題ないのですが、「商品取得API」というのは商品詳細を表示するページに必要な情報をBFF(API)でまとめて取得している点に違和感を私は感じています。
例えば、「商品取得API」が商品自体の情報を返すという機能としているにも関わらず、付随する情報、例えば価格の情報やタグの情報なども同じAPIで取得しています。 これはそれぞれの情報に対応する専用のAPI、例えば注文取得APIと注文履歴API、2回リクエストしたほうが再利用性が高いのではと思っています。
今後の方針としては、各APIは単一の責務を担当するというルールで定めようと検討しているのですが、奥野さんのご意見をいただきたいです。
誰のためのリファクタリング?
奥野 この話題はずっと議論されている鉄板中の鉄板の話題だと思うんですよ。 一回の通信でどの様にに返すべきか、結局は何を目指したいかによると思います。通信(ハンドシェイク)が増えれば増えるほど、当然レスポンスは遅くなっていきます。
BFFというポジションとして、直接別にデータベースとやり取りをしているわけではなく、別の外部のRESTfulなAPIやGraphQLなどから取得してくるということは、APIを分ければ分けるほどレスポンスは遅くなっていきます。 なので何を指標に置くかによると思っていて、分けることによって段階的な画面のレンダリングができるから、エンドユーザーにとって高速な表示が可能であるなのか、分けることで開発者が管理しやすいなのかによっても変わってきますね。 武市さんがこういうアーキテクチャをとろうという判断に、「誰のために」が欠けている気がします。
誰に対してプロダクトを作っているのかは、プロダクトを使ってくれるお客さんがいてこそだから、お客さんよりも自分たちの開発の利便性を優先するっていう判断は自分は怪しいなと思っています。 エンドユーザーに高速に提供するためにはこうなんだけど、トレードオフがあってやむを得ずここは分けないとツライ、みたいなジレンマはあると思います。そこを考えずに、まず開発のリファクタリングありきであったり、コードが細分化されている方がテストしやすいとか開発しやすい、というのはお客さんの方見てないな、と思ってしまいます。
武市 おっしゃるとおりで、トレタではVercelを使ってるのでキャッシュされるから、そこまでフォーマンス落ちないだろうみたいな、結構安直な考えだったところはありました。
奥野 キャッシュがあったとしても、情報ごとにキャッシュの鮮度というものがあって、頻繁に更新されないって分かってるんだったらキャッシュは有効でもいいけど、頻繁に更新される部分であったら、そもそもキャッシュは有効な手段ではない、となってくる。 例えば、画面上のレンダリングを分割して出したいとか、分割して一部の情報は一回取得すれば十分とか、ここの部分の情報は頻繁に変わるから頻繁にフェッチさせる必要がある、みたいに使いまわせる部分と、都度取得しに行かないといけない部分というのはあります。 モバイルオーダーアプリで言うと、タブとかあまり頻繁に変わらない部分もあるので一度取得したら十分だけど、在庫の情報は頻繁に変わるから都度取得しないと、売り切れなのに画面では販売中になっていたりしたら問題になてしまう。
どういった切り口でもいいですが、その開発はエンドユーザーのためにやってるのか、開発者のために行っているのか、運用コスト改善のためにやっているのか、とにかく指標を定めないまま、考えているような印象を受けたので、そこをしっかりと考えてから話を持ち出したほうがいいと思います。
武市 なるほど。ちょっと自己中的な考えだったことに気がつきました。開発者の目線しか見れていなかったです。
リファクタリングの目的を意識する
奥野 この時間はどうリファクタリングしていくかっという部分で議論してはいるけど、リファクタリングで開発生産性が上がることを目的にしてはいけないんですよ。
開発生産性が上がることが目的になるのではなくて、開発生産性が上がることによってエンドユーザーに届けることができるバグ修正だったり、機能追加が頻繁になることが目的だから、開発生産性を上げようではなくて、開発生産性を上げたことでエンドユーザーに価値を届けようなんですよ。
だからそこをリファクタリングを目的にするとかなり間違っているので、今回はリファクタリングを目的にする以前に、今回はAPIの分割を目的にしてしまっているから、しっかりとゴールを見据えないとなっという危うさは感じました。
武市 よくありがちな、手段が目的になってしまっているやつですね。
奥野 何のためにリファクタリングをしているのか、何のためにAPIをまとめる、あるいは分割するをやろうとしているのか、というその先にあるお客さんに与える価値っていうのを考えるというと思います。 そこを考えた上で分割すべきAPIを検討するだったり、統合して一個のAPIでまとめて返した方がいいのであればそれを検討するといいと思います。
武市 はい、ありがとうございます。視点がずれていたところを気づきをいただき、ありがとうございました!
コラム: REST API・GraphQL・BFFの変遷
今回は質問の答えには辿り着けませんでしたが、あらためてBFFやREST API・GraphQLなどが生まれてきた背景や目的を振り返ってみましょう。
2000年初期のWeb開発ではPHPやRubyOnRailsなど、サーバーからHTMLを返してレンダリングする方法が主流でした。RubyOnRailsではMVCモデルという開発パターンがとられており、ControllerがModelを基にHTMLであるViewを返す仕組みとなっていました。
そこからスマートフォンが流行りだし、アプリ開発が盛んになりました。アプリ開発の場合はアプリとAPIが分離されるため、アプリエンジニアはクライアント開発を行い、サーバーサイドエンジニアはAPI開発を行う分業化がされるようになりました。Webとアプリの両対応するケースも増えたので、Web側もアプリと同様の構成でAPIを呼び出すJavascript製フレームワークが登場しました。APIはマルチクライアントに対応したり外部に公開するケースも増えていったため、特定の画面に依存したAPIではなく汎用的に使えるRESTとしてリソースに準拠したAPI形式になっていきました。
APIがRESTになってくると、今度はクライアント側で必要な項目の足し引きが問題になっていきます。一覧画面では最低限の項目で十分ですが、詳細画面ではすべての項目および関連するリソースも必要となってきます。一つのAPIでそれらを包括して関連するリソースとそのネストしたリソースまで返却するようになると、データ量が多くレスポンス速度も下がってしまいます。一方で各画面に必要な項目だけを返すAPIを画面ごとに作っていくと開発量は増大していきます。そこで現れたのがGraphQLです。呼び出し側がクエリで必要な項目を指定することで最適なレスポンスをサーバー側の改修を必要とせず取得することができるようになりました。
一方、サーバー側はモノリシックな構成からマイクロサービスとしてデータベースやサーバーをドメインごとに分割する流れがでてきました。サーバーが分割されると必要なデータを取得するために複数サーバーへリクエストを必要とする場面がでてきます。そこで、データ取得や画面用にデータ加工をするサーバーとしてBFF(BackendForFrontoend)パターンが登場しました。クライアント側からはビジネスロジックを分離して表示処理に専念できるようになるため、フロントエンドの進歩により複雑化したこともあいまって必要性が高まりました。 BFFを入れることで通信は一回増えますが、HeadlessCMSなど外部APIも豊富になってきたため、クレデンシャル情報を持った処理を行うために中間サーバーを設けることも一般的になりました。
さらに現在の2024年においては、さらにBFFの在り方は変わりReact/Next.jsではSSR(Server-seide Rendering)やRSC(React Server Rendering)などサーバー側でレンダリングを行う流れが活発になっています。これは初期のPHPなどサーバーでHTMLを返していた頃と体系は似ており、技術トレンドが一巡したのかもしれません。