GraphQLを使って、ネイティブアプリにさまざまな集計方法のランキングを出す、というときについて考えている。
たとえば、ソーシャルブックマークアプリを作っているなら、「総合」「一般」「世の中」「政治と経済」みたいに、カテゴリごとのランキングを出すことがイメージできると思う。
どのようなqueryを用意して、どこまでパラメータ化するか、どこまで自由にするかによって、サーバークライアント間の責任分担や、その後の変更コストが変わってくる。
サーバーサイドで制御する
rankings: [Ranking!]!
みたいに、クライアントからは「ランキングください」とだけ送るパターンを考えられる。クライアントでは、Arrayの返ってきた順に画面上に表示する。
良い点
- サーバーサイドでランキングの定義を持てるので、APIだけでなく、ウェブの画面に表示するランキングなど、他の面との仕様を揃えやすい
- 変更がサーバーサイドで完結し、ネイティブアプリの審査無しでリリースできるので、流行ってる話題に応じて枠を編成したいときなどに、Arrayに新たなランキングを混ぜ込みやすい
- 管理画面に枠編成機能があって、そこで設定したランキングが配信される、みたいな形は普通に考えられそう
悪い点
- サーバーサイドでは、クライアントサイドでの画面上の並び順や、集計ロジック、足切り条件、などをサーバーサイドで知ることになる
- クライアントサイドでは、自由にクエリを組み立てて画面を作るというGraphQLの自由度が失われる
- 汎用的なAPIならクライアントの事情は知りたくないだろうけど、社内でアプリ用にAPIを作っているならAPIあってのクライアントなので、ここまでの自由さを求める必要はない?
クライアントサイドで制御する
technologyRanking: ranking(tags: ["programming", "iot", "golang"], threshold: 3, lastBookmarked: "2021-02-01")
みたいに、ランキングの詳細や集計のためのフィルタなどをクライアントから渡すパターンも考えられる。Elasticsearchに投げるクエリなんかをイメージすると、複雑なクエリでさまざまな問い合わせができることは考えられそう。
良い点
- クライアントで見せたい画面に応じて好きなクエリを送ってもらえば良いので、サーバーはクライアントの詳細を知らなくて良い
- 画面の都合に応じてランキングに新たなコーナーを追加する、といった改修がクライアントサイドで完結する場合がある
- ただし、新たな絞り込み条件やソートなどを実装したいときにはサーバーサイドを改修することになる
悪い点
- サーバーサイドでは、クエリの自由度が上がると負荷予測が難しくなる
- クライアントサイドでは、ただランキングを表示したいだけなのに、ランキング集計条件のような詳細な実装の詳細の話題を扱うことになる
- サーバーサイドが仕事をどんどん放棄していくと、GraphQL経由でSQLを発行できるようにしたので自由に叩いてくれ、となるけど、それはない、みたいな話
- 集計条件に手を入れたいときにネイティブアプリのリリースが必要となり、審査を通すなど、サーバーサイドのリリースよりコストが高い場面が多そう
- リリースしたアプリがユーザーの手元に行き渡るまで時間がかかるので、人によって見ているランキングが違うというタイミングが発生する
- サーバーサイドでデプロイする場合は切り替えはすぐに終わる
ところでマルチテナント型アプリケーションの場合
特定のテナント用のqueryを別のテナントで使いたいかどうか、という話があって、使う場合は、どの程度再利用するかも考えておく必要がある。
たとえばソーシャルブックマークを作っていたら、ほっこり動物ニュースだけが表示される動物ソーシャルブックマークアプリを開発し、世の中ランキングのかわりに、犬ランキング、猫ランキング、みたいなランキングを置きたくなりそう。
- queryは各テナントの事情を知らず、汎用的なqueryのみ用意する
- queryの型は同じだけど、テナントごとにランキング枠の一覧やその集計条件をサーバーサイドで知っていて、ランキング定義に合わせてランキングを返す
- テナントごとにqueryを分けてしまう。
example.com
ならexampleDotComRankings
みたいな- queryの定義は別でも、結果のtypeは再利用できそう
再利用するぞ、と意気込んで1で作ってみたけど、出来上がってみたものはクライアントから見てさして使いやすくない、という仕上がりになってしまった。2くらいが妥当なのかな、と思ってきている。
どういうことを考えていけばよいか
要はバランスということで、どういう特性があって、どういう性質が求められているか考えていくと、ほどよい塩梅を決められそう。
- どれくらい変えたくなるか
- 毎週のおもしろトピックとかを出していきたいならクライアントサイドに話題を書くことはできないのでサーバーサイドでやることになる
- サーバーサイド、クライアントサイドのチームの規模の大きさ
- リソースの潤沢にある側で改修したほうが全体的に見るとお得
- サーバーサイドは1人しかいなくて手が回ってない、ならクライアントサイドでやったほうが速度が出るかもしれない、とか
- リソースの潤沢にある側で改修したほうが全体的に見るとお得
- サーバーサイド、クライアントサイドのチームの近さ
- すぐ改修できる場合(コミュニケーション的に、とか、全員がなんでもできるとか)は、審査のないサーバーサイドでやったほうがリリースが簡単
- 地球の裏側で開発していてどうやっても半日待つことになる、どちらかのコンポーネントを別会社で開発していてコミュニケーションで苦労する、とかなら、自分たちが触りやすい方でやるとよさそう
ブラウザから使うAPIならリリースとともにすぐクライアントも入れ替わってくれる前提でよいので好きにコントロールできるけど、ネイティブアプリから使うなら古いクライアントがしばらく残ってしまう、ということに今日気づいた。いろいろありますね。
追記
Federationという概念を教えてもらった。
マルチテナントでいうとスキーマのfederationがApolloというNodeのサーバーサイド実装の拡張であるのでこれを検討しても良いかもしれません。複数のGraphQLスキーマをまとめてひとつに見せるやつなので、管理画面からはこっちを使うとかはフィットするかもです。
— 青木華絵 (@aereal) 2021年2月5日
www.apollographql.com