BFF (Backends For Frontends) の話

開発Div. 喜村です。

BFF、私としては、最近(?)よく名前を目にするようになったバズワード的な存在なのですが、 私はフロントエンドについては疎い方なので、こんな時に頼りになるGoogle Trendに聞いてみました。

f:id:jkimura:20180601012138p:plain

いい比較対象がなかったので、Vue.jsのフレームワークである、Nuxt.js と比較しました。
Nuxt.jsもいい感じに伸びてますが、"Backends For Frontends"も若干山があるのが見えます。
これを見ると、2016年代前半からWordとしてはあったようですが、その後落ち込んでいます。
ですが、最近また山が増えてきており、一番直近の山は最近の山の中で一番大きいようですので、また流行っていると言えるのではないでしょうか?

ちなみに、"BFF"で検索をすると、"Best Friends Forever"のノイズが入るのでダメです。
Backends For Frontendsは残念ながら女子高生に人気ではありません。

実際に検索してみると、2016年後半から、2017年前半にかけて、ベンチャー企業で導入してみた発表が出てきてますね。
最近も、LTなどで当たり前のようにBFFが出てきたり、こんなイベントがあったりとメジャーな存在になりつつあるような印象を受けます。

https://uit.connpass.com/event/88434/

というわけで、今回はそんな BFF (Backends For Frontends) を自分なりの解釈で話します。

BFF (Backends For Frontends) とは

その名の通り、フロントエンドのためのバックエンドです。
図にすると以下のような感じです。*1

f:id:jkimura:20180605014934p:plain

BFFで使われる言語としては、

  • ・フロントエンドエンジニアが実装する場合は、Node.js
  • ・バックエンドエンジニアが実装する場合は、 Golang、Rails

をよく見ます。 一般的には、Node.jsを使い、フロントエンジニアが実装しているケースの方が多い印象を受けます。

ちなみに弊社のあるプロダクトでは、以下のような構成です。
f:id:jkimura:20180605020514p:plain

マイクロサービスとは行かないまでも、機能ごとに複数のサービスが存在しており、
それぞれの結果をまとめて、フロントエンドが使いやすいように整形して返す責務をBFFが担っています。
弊社の技術スタック的にNode.jsに長けた人がいないので、Golangで実装しています。

BFFの責務・BFFを使う理由

感覚的な話です。
私は、各サービス設計時にも「単一責任の原則(Single Responsibility Principle)」が当てはまると思っていて、
各サービスでそれを実現しながらも、フロントエンドのパフォーマンスを落とさないためには、BFFは必然的に出てくるものだと思っています。

例としては、

  • ・ データ集計・分析サービスは、与えられたデータを集計・分析し、最適なフォーマットで結果を返す
  • ・ 全文検索サービスは、対象のデータから検索を行い、最適なフォーマットで結果を返す
  • ・ 認証サービスは、与えられたユーザー情報により認証を行い、最適なフォーマットで結果を返す

上記3つのようなサービスがあった場合、それぞれのサービスが思う最適なフォーマットは異なると思います。
また、フロントエンドが欲しいと思うフォーマットもそれぞれ異なると思います。

この時点で、フロントエンドが「欲しいと思うフォーマット」を取得するには、以下の2つの方法があるかと思います。

  • ・ それぞれのサービスが、フロントが欲しいと思うフォーマットに統一する
  • ・ フロントエンドが、それぞれのサービスからバラバラのフォーマットを受け取った後、自力でフォーマットする

それぞれ見ていきましょう。

それぞれのサービスが、フロントが欲しいと思うフォーマットに統一する

「単一責任の原則」の「クラスを変更する理由は1つ以上存在してはならない」が崩れます。
「データ集計・分析サービス」の責務は「データを集計・分析すること」であって、「先(フロントエンド)のことまで考えてフォーマットすること」でありません。

サービスを利用するフロントエンドが1つであり、フロントエンド:サービスが 1:1 だった場合はまだなんとかなるでしょう。
ですが、別のWebアプリケーションでも「データ集計・分析サービス」を利用することになり、フロントエンドが求めているフォーマットが違った場合はどうなるでしょうか?
新しいWebアプリケーションのフォーマットに対応するために、「データ集計・分析サービス」を修正しますか? 本来の責務である「集計・分析」に手を加えるわけではないのに?

個人的には、ちょっと気持ち悪いですね。

  • ・ 本来の責務とは逸脱した処理をそのサービスに持たせることになる
  • ・ サービスがフロントエンドを意識してしまっているので、密結合なシステムになってしまう

あたりが受け入れ難いです。

フロントエンドが、それぞれのサービスからバラバラのフォーマットを受け取った後、自力でフォーマットする

パフォーマンスに影響が出ます。
各サービスのフォーマットを、自身が求めるフォーマットに変換するのは、確かにView側の都合なのでフロントエンドの責務であると言えます。
ですが、ただえさえパフォーマンスに気を使わなければならないWebの世界で、そんな悠長なことをして入られますか?という疑問点は浮かびます。
また、多数のサービスに同時にリクエストを飛ばす際、HTTPの同時接続数制限である6はどうしてもネックになってきます。

なのでBFF

弊社ではそこをBFFにAPI Gateway的な責務を持たせることにより、双方の問題を解消することにしました。
もっと突き詰めると、API Gatewayを持つBackend(Golang)と、API Gatewayからのレスポンスに対してさらにフロントエンド向けに処理をするBFF(Node.js)に分かれると思いますが、 現状のプロダクトの規模だとこれで十分だと私は思います。

これで、各サービスの独立性は保ちつつ、フロントエンドのパフォーマンスを落とさずに、サービスとフロントエンドの連携ができました。
別のWebアプリケーションを作るときは、フロントエンドとBFFをセットで作れば、各サービスの修正をせずとも実装できるわけですね。

世間一般的なBFF

BFFの詳細な解説は、以下の記事が詳しいと思います。(すごく参考になりました。)

Sam Newman - Backends For Frontends

日本語記事だとこちらも、わかりやすく解説されていて読みやすいです。

BFF(Backends For Frontends)超入門――Netflix、Twitter、リクルートテクノロジーズが採用する理由 (1/2):マイクロサービス/API時代のフロントエンド開発(1) - @IT

まとめ

「なぜBFFを導入しようと思ったのか」を長々と書いてしまいましたが、結論は「書いててスッキリするので早く導入しよう!」だと思います。
もちろん、「一刻も早くサービスをリリースしたい」という過程では必要ないと思いますが、「今後規模が拡大するかもしれないサービスの設計」や、「既存のサービスを利用した新規サービス設計」などでは導入の価値は十二分にあるかと感じました。
BFFは、世間のマイクロサービス化の流れに乗ってこれから当たり前になっていくアーキテクチャかと思いますので、概念だけでも押さえておき、いざ出会った時に困らないようにしておきましょう。

*1:オリジナルのThe Go gopher(Gopherくん)は、Renée Frenchによってデザインされました。