2024.12.10
“放置系”なのにサイバー攻撃を監視・検知、「統合ログ管理ツール」とは 最先端のログ管理体制を実現する方法
Rust製の業務WebアプリケーションをRustでリプレイスのドサクサでBFF/FEを大整理した話(全1記事)
リンクをコピー
記事をブックマーク
Ken Ogura氏(以下、Ogura):ではOguraから「『Rust製の業務WebアプリケーションをRustでリプレイス』のドサクサでBFF/FEを大整理した話」をします。
私の自己紹介。フロントエンドエンジニアをやっています。もともとはバックエンドでRustバリバリ触ってたんですが、いろいろな事情で今はフロントエンドを触っています。あと趣味でいうと競技プログラミングが得意です。あとYouTuberもやってます。
私の(発表)時間はちょっと短いので、早めにスススといきます。あらすじでいうと、受発注プロダクト(として)、もともとリプレイスする前のものがありました。BFF(Backend For Frontend)とフロントエンドの開発体験がちょっと下がり始めていたというのがあります。そこでバックエンド刷新することになり、BFF/FEもこれみよがしに整理してやろうとなったというのがあらすじです。
(スライドを示して)今回話すのはここなので、Rustの話はこれからしばしばなくなってきますが、ちょっと我慢してください。
まずやった大整理その1です。「GraphQLをコードファーストに統一」というものがありまして。GraphQLに馴染みがない方のために、あとBFFという概念にもあまり馴染みない方もいるかもしれないので、そこも含めて説明します。
(スライドを示して)まず、一般にAPIってこういうかたちをしていますね。クライアントがあって、サーバーがあって、リクエストを飛ばせばレスポンスが返ってくるというふうになっています。
この2人がちゃんと会話ができるためには、間にスキーマが必要です。要は「リクエストはこういう型でお願いします。そしたらレスポンスこういう型で返します」みたいな情報があって、クライアントとサーバーがこれを参照することによって、ちゃんと整合性の取れた会話が取れるわけです。(スライドに)描いてあるのは抽象的な図です。
実際のリプレイス前、リプレイス後も同じですが、このようなスタックでやっていました。
左にあるのがフロントエンドです。右にあるのがBFF。BFFは今はただのサーバー、バックエンドだと思っても大丈夫です。GraphQLというフレームワークを使ってコミュニケーションを取っていました。
リプレイス前のプロダクトだと、最初はこのような依存関係でした。つまり、GraphQLのスキーマを書くわけですね。「こういうクエリをしたら、こういうリクエストを返しますよ」という型を書いてあげて、その定義したファイルをフロントエンドもバックエンドも参照して実装していくわけです。バックエンドはそのとおりにサーバーの処理を書いていくわけです。こういうのをスキーマファーストといいます。
この逆で、バックエンド、サーバー側がそのサーバーのコードを書くと、そこの型情報からGraphQLのスキーマを作ることもできるわけです。こういったものをコードファーストと言ったりしますが、諸事情でコードファーストとスキーマファーストが混ざってしまっていたというのが、リプレイス前のフロントエンド事情になっていました。
これだと依存関係がゴッチャになったり、ディレクトリ構造も少しなんか汚くなってしまって、開発体験が下がるようになっています。
「混在しているとマズいのでどちらかに統一しましょう」ということになって、今回のドサクサでとりあえずコードファーストに統一することにしました。ポリシーが統一されて開発体験が向上しました。これは局所的に見た時の話ですね。
今回したかった話はなぜコードファーストにしたのかということで、その謎を解明するためにドメインの奥地に向かってみましょう。
(スライドを示して)全体像を見てみるとこうなっています。今までフロントエンドとBFFの話しかしてこなかったんですが、本当はマイクロサービスに散らばったバックエンド群とgRPCでやり取りしてというものがあって。
先ほどとは様子が違いますが、右側もやってることは結局同じで、クライアントとサーバーがあり、あるスキーマに従ってやり取りしてるというふうになっております。
(スライドを示して)この中で、先ほどのスキーマとクライアント、サーバーの依存関係みたいなものを矢印で描いてみるとこうなるわけですね。かつてはこのような依存関係になっていたわけです。
左側のほうは先ほど説明しましたが、右側も同様にProtoBuf(プロトコルバッファ)で定義したスキーマがあって、それをBFF(が参照する)。今度はバックエンドから見たらnestはクライアントなわけですね。クライアントとサーバーが別のものを参照しているとなっていました。これで開発体験が悪くなっていました。
これをどう変えたかというと、GraphQL、nestはBFFですが、ここの依存関係をこうしたわけですね。こうするとすごく矢印の向きがいい感じがするわけです。
先ほどのスライドでも出てきたある図が思い浮かびますが、同心円を描きたくなるわけです。
こうすると、依存関係がすべてプロトバフの方向に向いてくれます。依存の方向性が統一されるのは、すごくクリーンな感じがしますね。いわゆるクリーンアーキテクチャというものも、「依存性を一方向にしましょうね」という思想が元ですから。
今回はアーキテクチャではありませんが、依存する先が1ヶ所ということはいいわけです。フロントでもTypeScriptを使っているし、バックでもRust使っているということで、複雑なドメインモデルに対応するためにすごく型が強い言語を採用してる節があります。
この時に大事になってくるのが、全体で一貫性が取れてることです。これが先ほどみたいに定義が2ヶ所でされており、その間の整合性を取らないといけないとなると、まぁコンパイルが結局全体で通ってればいいですが、通すのが大変になってきます。
それに対して一方向性が保たれていると、プロトバフをいじれば、そこに向かって矢印が伸びてる人はみんな巻き添い食らうわけです。それを順番に、前のほう、中心のほうからエラーを直していけば、ちゃんと筋の通ったプロダクトができるようになっていて、これもある意味生産性向上につながりました。
というわけで、このようなものを意識してバックエンドの人とコミュニケーションすると、結局自分たちに返ってくるんだとわかってうれしかったという話です。
今回の大整理1のまとめをすると、実装方法のポリシーが統一されてうれしいというのもあるけれど、構造がクリーンになった。そのため、責務の切り分けなどを意識しやすくなるわけです。で、すごくよかったという話がありました。
大整理その2。もう1個、大整理の話をします。リポジトリを1個にしたという話。モノレポ化して開発体験が向上した話があります。
そんなに深く話しませんが、(スライドを示して)もともとこういうふうに分かれてました。特に分かれてた部分が、gRPCのスキーマとバックエンドとフロントエンドたちが別々のリポジトリに分かれていて、お互いにサブモジュールのかたちで参照しているようになっていたんです。
正しい状態だとは思いますが、更新が大変なんです。gRPC、プロトコルバッファの定義を変えたとなったら、他の2つのリポジトリでもサブモジュールの更新をしなければいけません。
それを忘れていて「動かない」と言って「なんでだろう」と調べてみたら、ただ更新していなかっただけというのはちょっとゲンナリします。
それを忘れてなかったとしてもPRが3倍発生するので、すごく大変です。レビューの時間が取られちゃうと。CIなどもちょっと複雑になってきたりして、なんか嫌だなーとなってたところでモノレポ化して、すごくすっきりしたという話がありました。
この時の教訓ですが、クリーンアーキテクチャみたいなものとか、いろいろ話されてる、名前がついてるパターンに従って教科書的なサービス作りをしようとしていますが、そこでよく引っかかるのが、モノレポ化と疎結合性は相対する。反対の概念なんじゃないかという気がしますが、実はそうじゃないということが今回の統合でわかりました。
というのも、Backend for Frontend(BFF)をバックエンドとフロントエンドの間に持つのは、ある意味「疎結合な状態を保ちましょう」という気持ちの表れです。「それを全部一緒のリポジで管理するというのはどういうことだ」となります。
そのあたりのサービスの立ち上げなどは、CIで出し分けてるわけです。デプロイする時は完全に3つの仮想化された違うイメージが吐き出されて、それが別のコンテナで立っているわけですから。サービスとして動いてるものは疎結合になっています。
それに対してリポジトリが一緒かどうかは、そのサービスの疎結合性とはあまり関係ないですよね。ちょっと気を使う必要はありますが、別に保ちながらモノレポであることはできるので、疎結合にしないといけないということでサブモジュール地獄にする必要はないということを、今回学びました。
その他にも整理をしています。GraphQL周りの整理が多いです。
今までデファクトリになってるコンベンションがいろいろありましたが、それにあまり従わずにgRPCと同じ名前を付けるようなことをやって読みにくくなっていたので、コンベンションに従うようにしました。
GraphQLの思想としては、オブジェクトがたくさんあって、その間のつながりを記述するという感じのイメージがあります。そうすると、バックエンドにRustのマイクロサービスがたくさん立っていますが、それをオブジェクトのつながりというかたちでまとめあげるのがBFFの責務だということがだいぶ明確になって。
しかも、それを意識してない人でもそこにあるGraphQLの他のクエリやミューテーションの実装を見れば、「ああ、そんなふうに書けばいいんだ」とすごくすっきりするということがあり、すごくよかったです。
(スライドを示して)真ん中のものは反省です。GraphQLの専門用語が入っていてわからない方はちょっと申し訳ないですが、GraphQLのいいところは、フィールドレベルでリゾルバを書いたり、データローダーを使ったりして、N+1問題の解決の層をもう少しフロント側に寄せることができることです。
あまりその機能を使っていなかったせいで、バックエンドにいろいろなお願いを今までしてしまっていた過去があり、それと決別できてよかったです。
他にもCIをきれいにしたり、テストをちゃんと書くようにしたり、細々とした整理をしていました。
というわけで、そろそろ終わりなのでまとめに入ります。ドメインを型で守る時、型定義の一貫性を保つため、依存性を意識すると開発体験が向上するという話。
要は、先ほどのクリーンアーキテクチャのように、同心円があって、どこに型のオリジンがあるのかを意識して、そこに向かって依存するようにコードファースト・スキーマファーストを混ぜていきましょうということです。その結果、開発体験が実際に向上したというのが1個目。
2個目が、先ほどお話ししましたが、リポジトリをモノレポにするかを別々に分けるかということとサービスの結合度はまた別の概念だから、そこはちゃんと切り離して考えるといいよということ。
今回の私の意見としては、モノレポにするかどうかは、開発体験のウェイトを大きく検討していいです。PRが分かれちゃうと嫌だとか、シェアが面倒くさいことのほうが、サービスの結合度よりはモノレポにするかどうかに関わってくると思うので、そこを意識してみてください。
ということで以上です。ありがとうございました。
関連タグ:
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
2024.12.09
国内の有名ホテルでは、マグロ丼がなんと1杯「24,000円」 「良いものをより安く」を追いすぎた日本にとって値上げが重要な理由
2024.12.09
10点満点中7点の部下に言うべきこと 部下を育成できない上司の特徴トップ5
2024.11.29
「明日までにお願いできますか?」ちょっとカチンとくる一言 頭がいい人に見える上品な言い方に変えるコツ
2024.12.04
いつも遅刻や自慢話…自分勝手な人にイラっとした時の切り返し 不平等な関係を打開する「相手の期待」を裏切る技
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.12.06
嫌いな相手の行動が気になって仕方ない… 臨床心理士が教える、人間関係のストレスを軽くする知恵
2024.12.03
職場の同僚にイライラ…ストレスを最小限に抑える方法 臨床心理士が語る、「いい人でいなきゃ」と自分を追い込むタイプへの処方箋
2024.12.05
「今日こそやろう」と決めたのに…自己嫌悪でイライラする日々を変えるには
PR | 2024.12.04
攻撃者はVPNを狙っている ゼロトラストならランサムウェア攻撃を防げる理由と仕組み