🏝️

PPRはアイランドアーキテクチャなのか

2024/06/08に公開

先日、Next.jsの新たなレンダリングモデルであるPartial Pre-Rendering(以降PPR)について記事を投稿しました。

https://zenn.dev/akfm/articles/nextjs-partial-pre-rendering

この記事を書いてる時は意識してなかったのですが、感想でアイランドアーキテクチャに言及されるケースが散見されました。社内で上記記事の話題になった時も同様に、アイランドアーキテクチャとの違いについて問われました。

結論から言うと、PPRとアイランドアーキテクチャは全く異なるものです。本稿ではPPRとアイランドアーキテクチャの違いについて解説します。

PPR

まずはPPRとアイランドアーキテクチャの概要を改めて整理しましょう。

PPRはページをstatic renderingとしつつ、部分的にdynamic renderingにすることが可能なレンダリングモデルです。具体的には、画面をbuild時(もしくはrevalidate後)に静的生成しつつ、リクエスト毎に処理が必要な一部動的な部分を遅延レンダリングにすることが可能になります。Next.js App RouterではレスポンスがStreamベースなので、PPRの場合まず静的な部分が早期に送信され、遅延レンダリングが完了するたびに徐々に動的な部分が送信されて1HTTPレスポンスで完結します。

以下はNext.jsのドキュメントで紹介されてるECサイトにおける商品ページの構成例です。

ppr shell

具体的な挙動イメージは公式のDEMOページがわかりやすいので、下記にアクセスしてレコメンド情報やカート情報がスケルトンから置き換わる様子をぜひご覧ください。

https://www.partialprerendering.com/

PPRはNext.jsによって提唱され現在も開発中で、Next.js v15(RC)でexperimentalフラグ付きで利用可能です。Next.jsのPPRでは、<Suspense>境界をもってstatic/dynamic renderingを分けられます。

import { Suspense } from "react";
import { PostFeed, Weather } from "./Components";

export default function Posts() {
  // Suspenseの外側はstatic rendering
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        {/* PostFeedはdynamic rendering */}
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading weather...</p>}>
        {/* Weatherはdynamic rendering */}
        <Weather />
      </Suspense>
    </section>
  );
}

アイランドアーキテクチャ

一方アイランドアーキテクチャ(Islands Architecture)はAstroFreshなどで採用されてるクライアントサイドアーキテクチャです。これはパーシャルハイドレーションと呼ばれる、必要な部分のみをハイドレーションする手法をベースに構築されています。日本語ドキュメントだと、以下Astroのドキュメントがわかりやすいかと思います。

https://docs.astro.build/ja/concepts/islands/

アイランドアーキテクチャは画面全体を静的HTMLという広大な海に見立てて、独立したインタラクティブなUI郡を1つの島(アイランド)と捉えることで「広大な海にいくつかのアイランドが、独立して存在している様子」を比喩して命名されました。以下はPreactの作者のブログより引用したアイランドアーキテクチャの例です。

island architecture

色がついてるところがアイランドです。Headerにはハンバーガーメニューなど、Sidebarにはアコーディオンなどがあってインタラクティブということなのでしょう。カルーセルはインタラクティブな典型的要素です。色がついてないところはJavaScriptを必要としない静的なHTMLです。

PPRとアイランドアーキテクチャの違い

PPRとアイランドアーキテクチャはどちらも「静的」な部分をベースに一部を分離するという点で似ているようにも見えます。前述の図も見比べると画面全体を「静的」とし、一部を分離している様子なども似ているかもしれません。

しかし、PPRの主眼はサーバーサイドでのレンダリング、アイランドアーキテクチャの主眼はクライアントサイドでのレンダリングです。同じく「静的」という言葉を用いていますが、これらは「何が静的か」という点で全く異なる意味を持っています。

観点 PPR アイランドアーキテクチャ
主な視点 サーバー クライアント
主な最適化対象 TTFB JavaScriptサイズ
「静的」が指すもの static rendering JavaScriptを必要としないHMTL

PPRで「静的」と表現されるのはstatic renderingです。一方アイランドアーキテクチャで「静的」と表現されるのはJavaScriptを必要としない非インタラクティブなHTMLです。

Client Componentsとislandの比較

ここまでの説明を経てすでにお気づきの方もいるかもしれませんが、アイランドアーキテクチャにおけるアイランド相当な概念が、ReactやNext.jsの世界でも存在します。Client Componentsです。Server ComponentsにClient Componentsを埋め込んでいく様子は、まさしく静的HTMLの海に、アイランドを埋め込んでいく様子に近しいものです。

つまり、アイランドアーキテクチャはPPRではなく、RSC(React Server Components)アーキテクチャと近いしいものと言えます。

Astroにおけるレンダリングモデル

Client Componentsに対応するのがアイランドなら、Server Componentsに対応するのはなんでしょう?Astroで言うとこれはAstroテンプレートです。そしてPPRと比較すべきはAstroテンプレートやアイランドに対するレンダリングモデルです。

Freshについては未調査ですが、現在Astroにおいてはページ単位でのSSRとSSGがサポートされているようです。そしてちょうど先日、PPR相当のServer islandsの実装が検討され始めたようです。

https://github.com/withastro/roadmap/issues/945

これらをまとめると、Next.js視点でAstroとの関係を比較すると以下のようになります。

  • PPR: Server islandsサポート
  • Server/Client Components: アイランドアーキテクチャ

2層アーキテクチャの螺旋

さて、前述のようなNext.jsとAstroの比較はDan Abramov氏も言及しています。

https://x.com/dan_abramov2/status/1757986886390264291

  • Server and Client components
  • Astro Templates and Astro Islands
  • PHP partials and jQuery plugins

these are all examples of two-layer architectures.

Server ComponentsとClient Components、Astro TemplatesとAstro Islands、PHP とjQuery pluginsなど、これらは全て2層のアーキテクチャです。外側のレイヤーはサーバー側で実行され、内側のレイヤーはクライアントサイドで(も)実行されます。

アイディア自体は新しいものではなく、多くで使われている古きものです。しかしこれらは同一ではありません。それぞれに小さな進化が含まれ、時代と共に技術の螺旋を歩んでいます。

技術選定の螺旋

https://speakerdeck.com/twada/understanding-the-spiral-of-technologies-2023-edition?slide=10

  • 技術の変化の歴史は一見すると振り子に見える
  • でも実は螺旋構造。同じところには戻ってこない
  • 差分と、それを可能にした技術が重要

古くはPHPとjQuery pluginsで実装していたアイディアが今ではNext.jsやAstroで採用されている訳ですが、これらはユニバーサルなJavaScriptで実装できるというメリットと、さらにそれぞれ異なる螺旋的進化を経ています。

感想

PPRではサーバー側のstatic/dynamicな境界を設計する必要があります。一方RSCアーキテクチャではクライアント側でのインタラクティブ/非インタラクティブな境界を設計する必要があります。これら2層のレイヤーごとに設計を行う必要があることを意識すると、アイランドアーキテクチャとPPRは全く異なり、むしろRSCアーキテクチャと類似した技術であることが理解いただけるかと思います。そしてこの2層のレイヤーに分けて考える必要があることは、前回の記事でも言及しましたuhyoさんの言う多段階計算そのものです。

https://zenn.dev/uhyo/articles/react-server-components-multi-stage#一言でreact-server-componentsを理解する

こうやって日々深堀っていくほどこの多段階計算の理解が重要だと感じます。そしてこの多段階計算の1段目(サーバー側)と2段目(クライアント側)を分けて考えられると、複雑そうに見えるNext.jsのアーキテクチャがシンプルに見えてくるかもしれません。

筆者なりにまとめてみましたが、わかりづらい点などあればご指摘いただけたら幸いです。

Discussion