こんにちは。ファンと共に時代を進める、Web3スタートアップのGaudiyで、フロントエンドエンジニアをしているkodai(@r34b26)です。
Gaudiyでは、Airbnbが採用していることで有名な「SDUI(Server-Driven UI)」という設計手法を取り入れています。
先月のTech Blogでは、ユーザーに対してファンダムな体験を届けるために実践している、スキーマ駆動開発についてお伝えしました。
今回は少し視点を変えて、顧客やユーザーと対峙する社内メンバーに対して、ファンダムな体験を届けるために実践している、SDUIについてまとめてみます。
GaudiyでSDUIを取り入れた理由や、その実装方法なども書いてみたので、一事例としてよければご参考ください。
1. SDUI(Server-Driven UI)とは
SDUIはおそらく、Airbnbが使用する設計手法として、その名称とともに広がったのが始まりです。
UIの表現方法をすべてサーバーサイドで決定し、クライアントに返す、という手法になります。
通常のアプリケーション開発では、レンダリングロジック、ルーティングなどをクライアント側で決めますが、SDUIの手法ではこれらをサーバー側で決定しています。
日本語だとこちらのnoteがわかりやすいです。
2. SDUIのメリット
先述したAirbnbの記事や、一般的に説明されているメリットとしては、以下になります。
UIの変更にアプリケーションのデプロイを必要としない
サーバー側のロジックや設定を変更することで、クライアント側の表示を変更できるため、クライアントアプリのデプロイが必要ありません。そのため、デプロイまでの社内プロセスや実行時間を待つことなく、UIに対する変更が可能です。
アプリケーションの審査を必要としない
とくにネイティブアプリでのメリットになりますが、UI更新にデプロイを必要としないため、Apple, Googleの審査を毎回待つことなく細かい変更や巻き戻しができます。これにより、社内でコントロール不可能なタイムロスを圧縮することが可能です。
複雑な表示切り替えに拡張可能
レコメンドエンジンを用いたユーザーごとの表示切り替えによる、行動促進や広告配信ができます。また、ABテストや限定リリースなど、リッチなロジックによる制御を組みやすくなります。
つまり、クライアントの都合による変更反映までのタイムラグをできる限り減らすことができる。それがSDUIの最大のメリットです。
3. なぜGaudiyでSDUIを採用したのか
Gaudiyの話をすると、少なくとも現状はWebアプリケーション(PWA)のみで実装を進めており、ネイティブアプリの実装はしていません。またレコメンドエンジンに関しても、将来展望としてはありますが、現在は実装を行っていません。
それなのに、なぜSDUIを取り入れたのか。それは、顧客に提供するアプリケーションのカスタマイズ性を担保するためです。
こちらは、実際にGaudiyが提供している、各アプリケーションのホーム画面です。
同一ページ、同一ソースコードで実現していますが、表現がだいぶ異なっているのがわかると思います。
Gaudiyでは、ブロックチェーンを活用した「Gaudiy Fanlink」というプロダクトを通じて、さまざまなファン体験を総合的に提供していますが、そのコミュニティアプリは基本的に個別カスタマイズを行っていません。
プロダクトのめざす方向性や汎用性などの観点から、必要と判断された機能を追加する形で開発を進めていますが、クライアント企業によってはすべての機能を必要としていなかったり、段階的に機能を増やしていきたかったりするため、一部機能のみを提供する場合があります。
たとえば、
- 提供する機能を増やしたい/減らしたい
- ホームに表示するコンテンツを施策に合わせて変更したい
- IPの世界観を壊さないような形で表現したい
といったクライアントの要求に対して、Bizサイドのメンバーがエンジニアを常に頼ることなく扱えるようにしたい。それを実現するための手段として、SDUIを実践することに決めました。
4. SDUIの実装方法
ここからは、GaudiyでどのようにSDUIを実装しているかをお伝えしていきます。
元々、BFF-Client間通信に使用していたApollo GraphQLを生かしつつ、Airbnbの実装例を参考にしながら弊社のユースケースに沿うように、レイアウト、UIパーツをGraphQL Schemaに落とし込んで表現しました。
これによって、
- Fragment Colocationによるフェッチ最適化
- 宣言的データフェッチによるReactの宣言的UIとの対応
- 将来的に、宣言的なフレームワークを用いてネイティブに拡張可能
といった、データ効率とDXが高い開発が可能になりました。
では実際に、どのようなSchemaになったかを数点ご紹介していきたいと思います。
4-1. レイアウトのSchema
Gaudiyの現状のアプリでは、モバイルとデスクトップで、大きくレイアウトを変えることがあります。
具体的には、「モバイルで1列」「デスクトップで1列」「デスクトップで2列」の3パターンと、それぞれのレイアウトに対してトップに特別なコンテンツを置く・置かないの出し分けがあり、すべてのレイアウトは3 x 2=6パターンで表現されています。
これらを表現でき、また将来的にパターンが増えたり、タブレットなどに細かく対応する余地を持たせたSchemaとして、以下のように表現しました。
type LayoutsPerFormFactor { mobile: Layout! desktop: Layout! } union Layout = SingleColumnLayout | TwoColumnLayout type SingleColumnLayout { mainTop: SingleSectionPlacement main: MultipleSectionsPlacement! ... } type TwoColumnLayout { mainTop: SingleSectionPlacement main: MultipleSectionsPlacement! sub: MultipleSectionsPlacement! ... }
抽象化したパターンがSchemaから把握できる形になっていて、わかりやすいかと思います。
4-2. UIパーツのSchema
前述したコミュニティごとの特性の違いから、ある機能単位での表示・非表示と、UI上の表現の違いを数パターンで表したいという要望がありました。
たとえば、以下のような同じ”トピック”機能に対して、ランキング形式でユーザー行動をより強く促す形と、フラットに表現する形で使い分けています。
ここでは、機能ごとに出し分けする点と、機能ごとのpreview的に複数項目を表現する点から、機能ごとにセクションを分け、Arrayで子を持つように表現しました。
type SectionContainer { id: ID! sectionComponentType: SectionComponentType section: Section ... } enum SectionComponentType { TOPIC_CAROUSEL TOPIC_TRENDING ... } union Section = ChatSection | TopicSection ... type TopicSection { title: String! items: [Topic!]! ... } type Topic { id: ID! label: String! description: String! imageUrl: String! createdAt: Int! isDisabled: Boolean! ... }
機能の増減は Section
で受け止め、機能ごとの見た目や並びのブレに関しては SectionComponentType
で受け止めることで、unionが膨大に増えることを抑制しています。また、UIごとに必要になるfieldの違いに関しては、clientでのcolocationで定義することによって、over/under fetchが起きないようにしています。
このように、プロダクトの性質や、設計の採用目的によって重きを置くところが違うので、SDUIの思想をベースにしつつ、ユースケースにあった調整をした上で使用すると効果的だと考えています。
5. まとめ
今回はSDUIに関して、Gaudiyで実際にどう取り組んでいるかをまとめてみました。
特にフロントエンジニアの視点で考えると、Schema駆動でバックエンドと協力して設計・実装するのと同じくらい、もしくはそれ以上に、宣言するUIがデザイナーと認識の合った形になるように、こちらからヒアリングして提案することが大事になると思いました。
これから運用と拡張のフェーズを経て、引き続きSchemaを育てていきたいと思っています。
興味ある人がいれば、ぜひお話ししたいですー。
Gaudiyの技術選定について知りたい方は、以下の記事をご参考ください。