Chapter 12無料公開

Server Side Composition

okmttdhr
okmttdhr
2020.12.30に更新

Server Side Compositionは、その名のとおりですが、Server SideでFragmentsを結合するパターンです。

ここでは、筆者が知っている事例やライブラリの中でいくつかのアーキテクチャを紹介します。

Layout Server + Fragments Server

Layout Server + Fragments ServerはシンプルなServer Side Compositionです。

ここでのFragments Serverは、各Micro Frontendsチームが返すFragmentsを返すサーバーを意味しており、Layout Serverは、そのFragmentsを組み合わせ、最終的なHTML、CSS、JSを返す、RailsやPHP、Node.jsなどのサーバーサイドWebアプリケーションを意味しています。

まずは「Node.jsのLayout Server + Fragments Server」を考えてみましょう。

このケースは、JavaScriptで完結するので比較的自由度が高く、例えば、①Reactのコンポーネントを露出するFragmentサーバー、②Vueのコンポーネントを露出するFragmentサーバー、のふたつがあったとしても、Layout Server側でパースして結合することができます。

では、既存のアプリケーションがNode.js以外で書かれていた場合はどうでしょうか。実際はそういうパターンが多いと思います。

このケースでは、フレームワークに特化したFragmentsを扱うのが難しいため、HTML stringなどを受け取ってFragmentsを組み立ててゆきます。

どちらのパターンも一見うまくいきそうです。

しかしここで、コンポーネントにサーバーからデータを渡した上でSSRさせたい時を考えてみましょう。この場合、Fragmentsサーバー自体に、データを受け取ってレスポンスを返却するようなインターフェイスが必要になってきます。Fragmentsサーバーごとにバラバラなインターフェイスでは、使用者(Layout Server)側は混乱するので、まず、組織内である程度の共通認識を持っておく必要があるでしょう。また、HTML stringを返したいような場合は、データを受けた上でコンポーネントをレンダリング、string化するような実装をする必要があります。バージョニングもうまいことやりたいです。こう書くと面倒くさくなってきませんか。

Layout Server + Fragments Serverというパターンは、素直ですが、多様な要件に対応しようと思うと難易度が増してしまいます。

Layout Server + Fragment Gateway

Layout Server + Fragment Gatewayは、別のチャプターでも紹介したGateway Aggregationというパターンに似ています。ここでのFragment Gatewayは、Gateway AggregationでのAPI Gatewayと同じような役割です。複数のFragmentsへのアクセスを請け負い、Layout Serverから責務を分離し、インターフェイスの統一も担います。

具体的な例をみてみます。以下の図は、HypernovaをMicro Frontendsに使ったアーキテクチャです。

ここでは、Layout ServerがRails、Fragment GatewayがNodeでできています。

①Layout Serverでサーバーのデータをつくり、②それをFragment Gatewayにリクエスト、③その後Fragment Gatewayでデータをコンポーネントに流し込み、④データ込みのHTMLが返却される、というのがおおまかな流れです[1]

Fragment Gatewayの役割としては、Layout Serverから見た時にFragmentsの取得をAPIとして抽象化していることにあります。それにより、多様なFragmentsをSSR可能な汎用コンポーネントとして扱う口になることが可能です。また、Fragments側がどんなフレームワークを使っていようとも、Fragment Gatewayでその際を吸収することができます[2]

以上が、Layout Server + Fragment Gatewayの基本的なアーキテクチャです。このパターンでServer Side Compositionができるフレームワークはいくつかあって、OpenComponents、Podium、PuzzleJsなどがあります。

Tailorの場合

ここで、Tailorというライブラリを紹介します。

TailorはLayout Server + Fragment Gatewayというよりは、より高機能なLayout Serverと捉えることができます。Fragmentsを結合することにおいて、いくつか特徴的な点があります。

<fragment />

Tailorでは以下のようにすることでFragmentsを解決することができます。

<html>
<head>
    <script type="fragment" src="http://assets.domain.com"></script>
</head>
<body>
    <fragment src="http://header.domain.com"></fragment>
    <fragment src="http://content.domain.com" primary></fragment>
    <fragment src="http://footer.domain.com" async></fragment>
</body>
</html>

Fragmentsはそれぞれ非同期にリクエストされ、primary asyncとあるように、優先度も決めることができます。

Streaming

FragmentsはStreamで配信されるのですべてのFragmentsが完成するのを待つ必要がなく、Time to First Byteが早くなります。

Assets

SSRするFragmentsには、Rehydration (Isomorphic JavaScript
参照) のためのJavaScriptアセットと、スタイルのためのCSSアセットが必要です。

Tailorでは、Link Headerにアセット情報をレスポンスします。これによって良いことは、今読み込むアセットをTailorに一任できることです。例えば、以下のようなハッシュ付きのアセットのレスポンスを考えます。

fragment.ctfvygbh.js

この場合、クライアントのバージョンごとに異なるハッシュを生成しても、レスポンスされたアセットを読み込めばよく、キャッシュ戦略もとりやすくなります。

まとめ

TailorのようにLayout Serverの機能に特化したライブラリを使うと、Micro Frontendsの利用者側が考慮することを減らすことができ、かつ、TTFBやアセット管理に関して最適化を行うことができます。

Ara Frameworkの場合

Ara Frameworkは、Hypernovaをベースにつくられていて、Micro Frontendsを構築するCLI、モジュール群を提供します。その中でも、Server Side Compositionのアーキテクチャが特徴的なので紹介します。

全体像は以下です。

詳細はDocを見てほしいのですが、ここでは概要だけ説明します。

Strangler Pattern

まず、作者のMediumをみると、Ara Frameworkは、Strangler patternを意識したものであることがわかります。

例えば、RailsやLaravelでつくられたモノリスなアプリケーションを、少しずつMicro Frontendsに適したアーキテクチャにリファクタリングしていくような考えです。

(Microsoft Cloud Design Patternsより)

これを前提に説明してゆきます。

Nova Proxy

Nova Proxyは、レンダリングを担当している既存のアプリケーション(全体像で言うPHPサーバー)と、ブラウザからのリクエストの中間に位置し、Reverse Proxyとして動作します。

PHPサーバーでは、データ層と通信してバックエンドのデータ構築を行います。HTML構築の際に、予めプレースホルダーを埋め込んでおき、Nova Proxyに返します。

Nova Proxyでは、受け取ったHTMLをパースし、プレースホルダーに埋め込まれたデータをペイロードとし、Nova Clusterにリクエストします。そして、プレースホルダーを、返却されたFragmentsに置き換えることが仕事です。また、例えば、フォールバックやタイムアウトの処理もこの層の責務となります。

Nova Cluster

Nova Clusterは、このチャプターでいうところのFragment Gatewayと捉えることができます。

Nova Clusterは、Nova Proxyから複数のFragmentsへのデータをまとめて受け取ります。受け取ったリクエストをもとに、各Fragmentsに問い合わせ、HTMLを構築してNova Proxyに返す、というまでがNova Clusterの仕事です。

まとめ

このようなアーキテクチャを取ることで、既存のサーバーはMicro Frontendsに対する意識を減らすことができ、データの構築に集中することができます。さらに責務を分解し、既存のレンダリングに関するロジックを徐々にNova Proxyに寄せていくことや、バックエンド層をAPIとして切り離すことも考えられるでしょう。

メリット・デメリット

メリット

Server Side Compositionが達成できることの一つとして、SSRがあります。それによって、Time to First Byteへの効果や、SEO最適化が期待できます。Streamで実装できるのも強みと言えるでしょう。また、Fragment Gatewayを用意することで、例えば、クライアントから複数のFragmentsへリクエストすることがなくなり、通信を最適化することができます。このような、パフォーマンスやSEO要件は、アプリケーションによっては必須となり得るため、その場合はServer Side Compositionを実装する必要がでてくるでしょう。

また、既存にモノリスなサーバーサイドアプリケーションがあり、それをMicroservicesに分解したいような場合、フロントエンドとバックエンドを疎結合なシステムにしてゆくことが求められます。Server Side Compositionは、Ara Frameworkの例で示したように、レガシーなモノリスアプリケーションを少しずつリファクタリングしていくようなケースに柔軟に対応できます。

デメリット

デメリットのひとつとしては、お気付きだと思いますが、複雑度が上がることが挙げられます。クライアントに閉じないアーキテクチャを考慮する必要があり、サーバーリソースも存在してくるので、可用性やスケーラビリティについて設計する必要もあるでしょう。それらが結果的にサービスの品質と組織の開発効率を向上させるかは常に意識が必要です。

また、これはMicro Frontends全般にもいえるのですが、デファクトといえる技術がまだないのも事実です。組織によって既存のシステムや課題感が異なるため、各々が最適な実装をし、体力のある会社がOSS化しているような状況だと私は考えています(そのため、本チャプターで紹介したライブラリだけでもアーキテクチャは様々です)。ライブラリやフレームワークの設計思想を汲み取り、自社の課題にフィットするかを判断、そうでなければ、自分で作りきる技術力が必要です。

まとめ

本チャプターではServer Side Compositionについて紹介し、いくつかのライブラリのアーキテクチャにも触れました。Server Side Compositionを使うことで、現実の複雑な課題に対応する中、かゆいところに手が届く柔軟なアーキテクチャを採用できると考えます。

脚注
  1. hypernova-${client}(ここではhypernova-ruby)を使うと、hypernova/serverへのリクエスト、コンポーネントの読み込みを抽象化できます。詳細はGitHubを御覧ください。 ↩︎

  2. Microservicesの場合でも議論になるとは思いますが、フレームワークを揃えるかどうかは組織で共通認識が必要だと思います。まず、インターフェースとしてはFramework Agnosticである方が自然という見方もできます。各チームが独立して技術選定ができるというのも、Micro Frontendsのもともとの思想に近いものがあるでしょう。しかし、実際はフレームワークが違うことで考えることも増えますし(結合レイヤーの複雑度、バンドルサイズ、社内での車輪の再発明)、「ギルド」的な情報共有ができるようになるというメリットもあります。使用者側のユースケースを考えて、どちらのほうがより適しているか考える必要があると筆者は思います。 ↩︎