Next.jsのRSC & Server Actionsを使ってみた所感
コンポーネントのカテゴライズ
いわゆるSPAが出たばかりの頃のコンポーネントはFull CSRであって、クライアントでしかレンダリングされないコンポーネントだった。
その後出てきたSSRやSSG(Jamstack)やその亜種(ISRなんかも)においては、コンポーネントをサーバーでもレンダリングできるようにすることで初回ロードにおけるパフォーマンスを向上させたりSEOの弱点を克服したり動的なアプリを静的化することでスケール可能にする手法だったりだった。
今回出てきたRSCはコンポーネントをサーバーでしかレンダリングさせない手法だった。
疎結合の苦労
これまでのアーキテクチャだと不必要な疎結合化を求められる機会が多かったように思っている。具体的にはモダンフロントエンドを導入した瞬間にAPI設計が求められるという点だ。
フロントエンドのライブラリを使って動的なアプリを作ろうとすると、REST APIやGraphQLのようなインターフェースを通じてフロントエンドとバックエンドとでAjaxを使って連携するのが必須になっていたが、1つのアプリケーションの中でわざわざ疎結合にする必要があるのか、という点が常々疑問だった。
もちろんJamstackのようにAjaxを必要としないアーキテクチャもあるが、コンテンツ更新のたびに再ビルドが必要なので更新経路が1方向(管理側で懇親するだけとか)のアプリのような限定的な状況でしか導入できなかった。再ビルドなしにアプリを動的化しようとすると結局はクライアント側からのAjaxの経路を確保する必要があった。
フロントエンドとバックエンドとを疎結合にすると、実質2つのアプリを開発する必要があるわけだが、本当にここまで疎結合にする必要があるのだろうか。
もちろんプロダクトがスケールして複雑化するといつかは疎結合化の必要性が出てくるケースもあるだろう。だが現場を見渡してみると、明らかに開発の初期フェーズにおいて当たり前のように疎結合化している光景をよく見かける。
本音としては、疎結合化が目的ではなくて、柔軟なUIを表現しようとするとモダンフロントエンドのエコシステムが欲しくなるが、フロントエンドを導入した瞬間にAjaxの経路を確保する必要が出てくるため、やむをえずREST APIやGraphQLを導入する羽目になった、ということじゃなかろうか。
バックエンド寄りかフロントエンド寄りか
プロジェクトが大きな収益を上げていた開発リソースも潤沢にあれば、フロントエンドとバックエンドとで疎結合にするのは全然問題ないと思う。ただ、観測範囲のほとんどのプロジェクトでは開発リソースがそれほど大きくなくても疎結合を強いられているプロジェクトが多く見える。一方でバックエンド単体に寄せられるかというと、社内でしか使わないようなシンプルなUIが許容されるサービスじゃないと難しいのではと思う。一度快適なUIを使うとそれがベースになってしまうし、ユーザ向けのアプリだとレスポンシブ対応やアクセシビリティなど要求水準がどんどん上がっていっている。
便利なUIを一度使ったあとに(謙虚に言って)シンプルなUIを使うのを強いられると、一応使えるけどなんか不便、という感想をどうしても抱いてしまう。
モダンフロントエンドをあえて使わずにLaravelやRailsのようなFW一本でアプリを作るというのはスピード優先なシチュエーションでは現実的な解だと思うが、それを理由にモダンフロントエンドを捨てるというのもやはり悩ましい。むしろサービスの開発初期だとバックエンドはシンプルでOKだけど、UI側はちゃんと見栄え良く作っていてほしい、という期待のほうが大きい気すらしている。
そういうシチュエーションで、アーキテクチャが密結合でも良いので、煩わしいAPI定義などを考えずに、フロントエンド寄りのFWで1つのアプリを作れる選択肢があっても良いと思っていたが、RSC & Server Actionsがそれではないかと思っている。
LaravelやRailsのテンプレートエンジンベースが、バックエンド寄りのフルスタックフレームワークだとすると、Next.jsのRSC/ServerActionsを使った開発はフロントエンド寄りのフルスタックフレームワークと言えると思う。
RSCが解決したもの
RSCによって、サーバーでしかレンダリングされないコンポーネントが登場した。これによりDBなど本来サーバーから直接じゃないとさわれない領域にコンポーネントのコードからアクセス可能になった。クレデンシャルも保持することができるようになった。ちなみに一度クライアントに渡ってしまったあとのUIにおけるイベント処理(ボタンを押して保存したり)については、たとえRSCであってもAjaxと同じ経路が必要になるはずだが、これはServer Actionsによって解決した。イベント処理時のAjax処理をフレームワークが透過的に処理することでエンジニアが意識しなくてもクライアントとサーバーとが連携できるようになる仕組みだ。ReadについてはRSCが、WriteについてはServer Actionsが担うというわけだ。
ServerActionsがAPIを透過的に処理するのは点だが、フロントエンドとバックエンドとで疎結合なアーキテクチャで開発しているとAPIを定義するのに大きなオーバーヘッドを感じていたため自然な解決方法のように思える。
活躍するシチュエーション
疎結合から密結合への先祖返りだとかいう指摘もある。もちろんアプリが大規模化するにつれてアーキテクチャは変更を余儀なくされるので全てのフェーズにおいて利用価値のあるものではないと思う。プロダクトのフェーズにおける適切なアーキテクチャというものがあるはうだ。RSCを始めとした技術は0 => 1フェーズにおける開発や、それ移行のフェーズにおけるBFFがメイン用途だと見ている。
思うところとしては、大してユーザ数も多くないのに中規模 ~ 大規模スケールを想定した疎結合アーキテクチャを採用しているプロダクトや開発チームが思ったより多いように見えるので、一旦先祖返りして1つのFWの中でUI ~ DBを採用したほうが楽なケースにおいて採用してみても良いかなと思っている。
先祖返りしているように見えて実は螺旋を描いていることもあると思う。