この記事は GraphQL Advent Calendar 2020 14 日目の記事です。
前回の記事は @joe-re さんの 「ライブラリの実装からCursor-based paginationにおけるcursorのフォーマットのベストプラクティスを探る」 でした。
前置き
GraphQLは2010年代後半に出てきた技術の中でも個人的に特に強力なアプリケーション実装パターンの一つだと思っているのですが、シンプルな実装なのに利用用途が豊富にあることと利用する立場が違うと全く印象を抱く事から全体像を掴みづらく、本来持つべきポテンシャルに対してまだ認知が広がっておらず利用されていないように感じます。
今回はサーバーサイドからの視点を中心にGraphQLを構築する要素を分解して解説するのとともに、それを利用した際にWebアプリケーション開発やそれに関わるエンジニアに起きうる変化について書いていきたいと思います。端的に言うと、これまでフロントエンドの人、WebAppの人、Serverlessの人、Microservicesの人がそれぞれ分断されていたけどGraphQLによって全部のナレッジが繋がる!みたいな話です。
GraphQLの全体像や利用される背景、フロントエンドとバックエンドの双方の課題とメリットみたいな記事を前回書いたのでそちらも参考にしてもらえると幸いです。 参考: GraphQLの全体像とWebApp開発のこれから
GraphQLを構成する設計パターン
GraphQLを構成する要素は以下のものに分解できます。
- フロントエンド分離
- API Gateway パターン
- Serverless, Microservices
- API Gateway パターン, Serveless/Microservicesパターンの応用としてのgRPC
これらの課題と解決策を見ていくことでGraphQLを理解していきたいと思います。
その前に, Query LanguageとしてのGraphQL
よく語られている部分なので手短に
- リクエスト, レスポンスに型がある
- self-documenting
- クライアントが要求した通りにデータが変えるのでオーバーフェッチを防ぐことができる
- リクエストを束ねる事でサーバーとの通信回数を減らす事ができる
今回はそれ以外の視点で見ていきます。
フロントエンド分離
GraphQLを使うということはつまり前提として、フロントエンドとサーバーサイドが分離していてHTTPで表現されたAPIを利用しているということ、となるためあえて入れました。
PresentationDomainSeparation
・プレゼンテーションロジックとドメインロジックが分かれていると、理解しやすい
・同じ基本プログラムを、重複コードなしに、複数のプレゼンテーションに対応させることができる
・ユーザーインターフェイスはテストがしにくいため、それを分離することにより、テスト可能なロジック部分に集中できる
・スクリプト用のAPIやサービスとして外部化するためのAPIを楽に追加できる(選択可能なプレゼンテーション部分で見かける)
・プレゼンテーション部分のコードは、ドメイン部分のコードと違ったスキルと知識が必要
サーバーサイドからフロントエンドを分離する理由もPresentationDomainSeparationで語られている課題と同じです。サーバーサイドとフロントエンドではスキルや知識が全く違うこと、実装のライフサイクルが異なることから、もしサーバーサイドがプレゼンテーションレイヤ(主にHTMLの書き出し)を行う場合は、まず最初に分離するべき箇所はHTMLの吐き出しをサーバーサイドから切り離すことでしょう。
フロントエンドのベストプラクティスがフロントエンドフレームワークに集約してきている
分離されたフロントエンドとして2017年以降、next.js, Nuxt.jsが台頭してきていてこれらにフロントエンドのベストプラクティスが集約しつつあります。リッチなデザイン、画面数の多いアプリケーションを実装する場合は、これらのフレームワークがメインストリームになってきています。これらのフレームワークを使うことでモダンなフロントエンド実装でのベストプラクティスを用いて実装することが出来ます。
最近のフロントエンドの動向については@mizchiさんの最近の資料を見るといいと思います。
Frontend Study #1: 基調講演 - Frontend 領域を再定義する
GraphQLを利用することで
GraphQLに限ったことではないですがサーバーサイドがAPIを用意し、フロントエンドを分離することでより生産性高く、モダンなフロントエンド開発のベストプラクティスに乗っかることが出来ます。
API Gateway Pattern
出典: https://www.apollographql.com/docs/apollo-server/
ServelssやMicroservicesなどの設計パターンではよく利用される設計パターンと実装です。APIの数が多くなる場合や、クライアントが多くなる場合にはAPI Gateway Patternを利用することで解決することが出来ます。API Gateway Patternはサブセットとして、ゲートウェイルーティングパターン、ゲートウェイ集約パターン、ゲートウェイオフロードパターンを内包します。
出典: API ゲートウェイ - Azure Architecture Center
URLベースAPI GatewayのOSS実装としてKong GatewayやKrakenDがあります。Kurbernetes界隈ではIstio Ingressが使われるみたいです。クラウドではAzureにはAzure Application GatewayやAzure API Management, AWSにはAmazon API Gatewayがあります。
API GatewayはL7のロードバランサの特徴を引き継いで実装されています(わかりやすい例としてKong Gatewayはnginxの機能拡張として実装されています)。API Gatewayの機能としてわかりやすいのでKrakenDの機能一覧を見てみます。
- モニタリング
- ロギング
- スタッツ
- セキュリティ
- SSL
- セキュリティポリシー
- スロットリング
- Rate Limit
- ユーザークォータ(使用量)
- Proxy
- OAuth
- プロトコルトランスレーション
- ロードバランシング
- QoS(Quoerity of Service)
- 並行呼び出し
- サーキットブレイカー
- Grained Timeout
- Cache
- キャッシュヘッダー(HTTP)
- Aggregation
- Merge sources
- Manipulation
- Transform
- Filtering
- Whitelist
- Blacklist
- Decoding
- From JSON, XML...
これらの本来はAPIサーバーで行っていた処理、特にmiddleware層に当たる処理、をゲートウェイに集約することでAPI Gatewayに一任することができます。
API Gatewayを利用する場合のサーバーサイドへの実装の影響
API Gatewayがmiddlware層の役割を肩代わりすることで、後方のアプリケーションサーバーではmiddleware層を薄く実装することが出来きます。バックエンドのアプリケーションが複数存在するServerlssやMicroservicesのパターンの際に採用することで、ドメインロジックを担当する各アプリケーションが似たようなmiddlewareの実装をせずに済みます。
API GatewayとしてのGraphQL
先のAPI Gatewayの実装ではmiddlewareが行うべき処理に徹する事で出来るだけドメインロジックを持たないように単純なL7ロードバランサのように実装・利用されますが、GraphQLではアプリケーションとしてAPI Gatewayの役目を実装することになります。任意のプログラミング言語で書くことができます(特に理由がなければJavaScript/TypeScriptがオススメ)。上で挙げたようなAPI Gatewayとしての全ての機能を最初から持ち合わせているわけではないですが実装して追加します。GraphQLのエコシステムが発展するとともにこれらのライブラリは拡充されていっているのでそれらを利用することで実装できます。
GraphQLを利用することで
GraphQLはAPI Gatewayの特徴を引き継ぐためAPI Gatewayを利用したときのメリットを享受することが出来ます。
Serveless, Microservices
GraphQLとServerless, Microservicesは技術要素としての直接的な関係はありませんが、Serveless, Microservicesを利用する際にはAPI Gatewayを採用するのが一般的です。GraphQLがAPI Gatewayとして機能するため、GraphQLの特徴としてServerless, Microservicesの特徴が語られる事があるので紹介します。
Serverlessとは
- サーバーレス アプリ: アーキテクチャ、パターン、および Azure の実装
- サーバレスパターン - AWS
- サーバーレスのおさらい - AWS
- Serverless Architectures - martinfowler.com
サーバレスパターンの定義は色々言われてますが個人的な解釈は、"クラウドプラットフォーム(AzureやAWSやGCP)で用意されたPaaSやFaaS, データベース, データストアを使って組むアプリケーション実装"のパターンです。
Microservicesとは
AWSの定義から引用すると
マイクロサービスは、小さな独立した複数のサービスでソフトウェアを構成する、ソフトウェア開発に対するアーキテクチャ的、組織的アプローチです。各サービスは、正確に定義された API を通じてやり取りします。これらのサービスは、小規模の自己完結型のチームが所有します。
マイクロサービスアーキテクチャはアプリケーションのスケーリングを容易にし、開発期間を短縮するため、イノベーションの実現と新機能の市場投入の加速につながります。
ServelessやMicroservicesのメリット
これらのアーキテクチャパターンの共通点は
- 担当するドメインロジック毎にプログラミング言語を選択できる
- 担当するドメインロジック毎にデータストアを選択できる
- 部分的にスケールアウト、スケールアップできる
- 責務分離がなされた設計になる
- ドメインごとに開発チームを分離できる
- 大規模、エンタープライズの開発に耐えうる
などがあります。(もっとあると思うのですが知りたい場合は、MicroservicesやServerlessの資料に飛んでください。)
APIのPresentationDomainSeparation
GraphQLを利用するクライアントとのインターフェースとなるAPIのSchemaをGraphQLが担当します。これはAPIのPresentationDomainSeparationであり、バックエンドアプリケーションはドメインロジックにのみフォーカスできることから開発工数を下げれると同時に、変更に強いAPIを実現できます。ドメインロジックからプレゼンテーション層が分離されているためドメインロジックの差し替え、バックエンドアプリケーションのアーキテクチャの変更もクライアントに影響を与えることなく行うことが出来ます。
HTTPオフローディング
API Gatewayを導入する場合、クライアントとのやりとりはAPI Gatewayが担当します。フロントエンドやネイティブアプリとサーバーとのやりとりは基本的にはHTTPSが採用されるのが一般的ですが、HTTPSはプロトコルとしては全般的な用途に耐えうる設計のためオーバーヘッドが多く、また通信を確立するためのハンドシェイクにもかなり時間がかかります。そのためMicroservicesパターンを採用する際にはHTTPSの代わりにgRPCが採用される事があります。これはゲートウェイオフロードパターンにあたります。gRPCは次で紹介します。
※ HTTPSをHTTPにオフローディングすることもありますが、プライベートネットワークで完全に区切られたクローズドな環境での用途ならならまだしも、好き勝手クラウド上にアプリケーションを展開する昨今、どこのネットワークを通っているかわからないサーバー間通信でHTTPを採用するのは危険です。
GraphQLを利用することで
Serverless, Miroservicesへの発展がしやすくなります。開発初期でもドメインごとに言語やデータベースを任意に選択できることができるため、無理した設計になりにくい事がメリットになりそうです。
蛇足: Lambdaのコンテナイメージサポートによる開発へのインパクト
最近Lambdaがコンテナイメージをサポートしました。DockerのコンテナイメージをLambda上に立てる事が出来ます。これによって起きることの妄想としては、
- 例えばStripe決済のAPIを実装したLambdaコンテナ
- 例えばCloudinaryのような画像と寸法を渡したら最適な画像を生成してくれるLambdaコンテナ
- 例えばOAuth2.0を実装したLambdaコンテナ
みたいなのがOSSとして公開される時代が来るかもしれません。そうするとドメインロジックで書く必要のあるコードが更に減っていくなぁと妄想してます。
gRPC
これまたGraphQLとは関係ありませんが、API Gateway パターン, Serveless/Microservicesパターンを利用している際に利用されるgRPCについて言及します。
GraphQLはあらゆるデータソースに繋がりますがその中でのgRPC利用を見ていきたいと思います。
gRPC (gRPC Remote Procedure Calls) は、Googleが開発を開始したオープンソースのリモートプロシージャコール (RPC) システムである。gRPCは、HTTP/2をトランスポートとして利用し、Protocol Buffersをインタフェース記述言語として利用する。gRPCは認証や双方向のストリーミング、フロー制御、ブロッキングとノンブロッキングのバインディング、取り消しとタイムアウトといった機能を提供している。多くの言語でクロスプラットフォームのクライアントとサーバーのバインディングが利用できる。
Wikipedia
gRPCはHTTP/2をトランスポート層として利用しますが、gRPCクライアントとgRPCサーバー間のアプリケーション層でのgRPCプロトコル通信ではオーバーヘッドがかなり少なくハイパフォーマンスに通信することが出来ます。gRPCのアプリケーション層ではHTTPSを意識しませんが、インフラレイヤではHTTPSのアプリケーションと思って構築すれば良いです。gRPCであればHTTPSのセキュアな通信の特徴とオーバーヘットの少ないサーバー間通信を両取り出来ます。
gRPCは名前の通りRPC, リモートプロシージャコール、遠隔関数呼び出し、なので別のインスタンスで動くアプリケーション上の関数を呼び出すイメージです。gRPCサーバーアプリケーションで考える必要があることは関数として引数と戻り値(値と例外)とドメインロジックのみです。大半メジャーな言語で書くことができます。
gRPCのより詳細な内容はgPRCのドキュメントに進んでください。
これまでのHTTPアプリケーション
HTTPアプリケーションのフレームワークを話すときに以下の図を見たことあると思います。
出典: Pylons のコンセプト
出典: The Clean Architecture - The Clean Code Blog by Robert C. Martin (Uncle Bob)
HTTPアプリケーションでは
- HTTPリクエストとレスポンスのハンドリング
- HTTP HeaderやCookieを利用した、認証やCacheやSessionなどのリクエストMiddlewareの実行
- URLとロジックのルーティング
- ドメインロジックの実行
- レスポンス middlewareの実行
- HTTPレスポンスの構築
のように複数の関心事があります。またそれらを分離するためにクリーンアーキテクチャのような分離レベルが考案されてきました。HTTPを扱う層やその他のmiddlewareが重厚なためわざわざこんな図を用意する必要があったわけです。
GraphQLとgRPCを採用したときの責務分離
GraphQLがAPI GatewayとしてHTTPSを喋り、middlewareを集積することでクリーンアーキテクチャでいうExternal InterfacesとGateways, Controllers, Presentersの層を担当。gRPCを採用したドメインロジックを受け持つサーバーはコアロジック、ServiceやUseCase, Entityにのみ注力する単純な関数として表現できるようになります。不要なコンテキストを一切持つ必要がないためHTTPフレームワークのような重厚なライブラリを必要とせず、ほとんどプログラミング言語で書くプレーンなコードだけでコーディングできるようになります。
gRPCサーバーアプリケーションはFaaSの書き味と近い
クラウドプラットフォームで利用可能なFaaSであるAzure FuncitonsやAWS Lambda、GCP Cloud Functionsではプラットフォーム側に様々なトリガーやインプット、アウトプットが用意されていて、コーディング自体は単純な関数を書きます。これらのFaaSとgRPCの書き味が似ているため、似たような開発エクスペリエンスで実装することが出来ます。
GraphQL + gRPCを使うことで
ドメインロジックを担当するアプリケーションサーバーはよりドメインロジックにフォーカスして書けるようになります。またパフォーマンスも出ることから開発終盤の完成形により近づきやすくます。
GraphQLを構成する設計パターン まとめ
GraphQLはAPI Gatewayパターンの側面を持っています。そのためServerlssやMicroservicesパターンに発展しやすく、またサーバー間通信で利用されるgRPCを利用しやすい環境です。GraphQLとgRPCを利用することで、ドメインロジックを担当するサーバーからHTTPプロトコルを分離することが出来、小さな関数でロジックをかけることがわかりました。
すでにRESTFulなAPIを持つHTTPアプリケーションが存在する場合はまずはGraphQLからはHTTPSで繋げば良いです。もし変更する実装コストを払える、またはレスポンス速度を改善する必要がある場合はRESTなHTTPからgRPCに段階的に切り替えて行くのはありかもしれません。
これらの状況を踏まえた今後のWeb開発に対する考察や考え
ここからは僕が考える、もしかしたらこういう世界線が来る可能性もあるなぁ、という考察を共有していく感じにしたいと思います。
GraphQLはサーバーサイド実装のベストプラクティスとなるか
世界の巨人たち(Microsoft, Google, Amazon, Facebook, Twitter, Airbnb, Uber, Netflix等々)はGraphQL, Microservices, Serverlessに移行していてその周辺のエコシステムを通してベストプラクティスを集約させて来ています。
それらのエコシステムに構築されたモダンWebのベストプラクティスに乗る最短コースがGraphQLを使い始めることです。MicroservicesパターンやgRPCの採用は少し前までは超大規模向けのソリューションでしたが、その距離をぐっと近づけて小さなチームでも採用できるまでになってきたのがGraphQLの隠れた功績かもしれないなぁと感じています。
実はこのあたりの、巨人の肩に乗る、というのがアーキテクチャ選定では大事になるのかもしれませんね。
HTTPアプリケーションを書く機会が減る..かも?
gRPCのパートで話したことですが、GraphQLがHTTPを担当し後続のドメインロジックを担当するアプリケーションがgRPCを利用する場合、アプリケーションエンジニアが実装する領域がFaaSかgRPCアプリケーションになっていく可能性はあると思います。HTTPアプリケーションより単純な関数に近いそれらは開発工数がずっと少なく済むため生産性、開発のエクスペリエンスも上がるためです。サービス全体でHTTPをしゃべるのがGraphQL1台で、後方のアプリケーションはHTTP不要になります。
ではRailsのようなモノリシックHTTPアプリケーションは不要になるのか。なりません。モノリシックアーキテクチャパターンは0→1を創る、小さなチームのための設計パターンです。当初はチームメンバーが少ない、数年続くかわからない、変更される可能性が高い場合においてはモノリシックアーキテクチャを採用するのが良いと思います。最初から数年続く事がわかっている、最初から開発人数を揃える事ができるような場合では採用をパスして最初から大規模開発に用いる設計パターンを選択すれば良いと思います。
モダンの開発手法では段階的な変更ができるソリューションが揃ってきているので、然るべきタイミングでGraphQLやMicroservicesのパターンに発展させていけばいいと思います。
RailsからgRPCへの移行はCookpadさんの事例が参考になりました。
参考: クックパッドがgRPCを採用するまで
サービス間通信で抱えていた課題と、RubyでgRPCを運用するための工夫
また、個人として持っている段階的アーキテクチャ変更の戦略を共有しておきます。
Rails/LaravelをAPIとして使ってるとして(そうでなければフロントの分離が最初)、Railsが抱えてる機能を分離していきます、OAuthはAuth0等のIDaaS、非同期ジョブはServerless化、APIインターフェース全段にGraphQL導入、APIを段階的にgRPC + 型あり言語に置き換え。最終的にRailsは引退させます。
— さぼ@EBILAB 👨💻☕️🎹🎧🐈 (@saboyutaka) December 14, 2020
余談ですが、Serverlssを採用している会社に入社する未経験のエンジニアはFaaSでの実装が多いためサーバーやアプリケーションの概念が薄いという噂を聞きました。今後は分離されたアーキテクチャで構成されたシステムでビジネスロジックを書くエンジニアのスキルセットからHTTPは不要になる世界線が来るかも...しれません。
GraphQLはフロントエンド・サーバーサイド、双方の分断の間に入る橋渡し役になれるか
フロントエンドとからのサーバーサイドへの不満の大部分がPresentationDomainSeparationがなされていないサーバーサイドがプレゼンテーションを握っている状態か、APIのインターフェースの定義が分かりづらい、ドキュメントがない、分散していることのように見えます。
GraphQLはそのような双方の不満を解決するために存在するアーキテクチャなのでGraphQLのようなAPI Gatewayの採用が増えてフロントエンドとサーバーサイドの分断がなくなればいいなぁと思ってますまる
GraphQLの採用タイミング
GraphQLは開発当初導入可能でから開発終盤まで常に使い続けられる息の長いアプリケーションにそうです。また段階的導入できるためどのタイミングからでも使い始めることが出来ます。
GraphQLの実装自体はとてもシンプル
いつも言ってることですが、とても簡単な仕組みで出来てます。 Apollo Serverのチュートリアルを1, 2時間やるだけで実装の感覚はつかめると思います。
GraphQLを触ったことある人はだいたいGraphQLのメリットを同じように共有してる感覚ですが、まだ触っていない人がGraphQLを情報だけで語ることでややこしい感じになっている印象はあります。GraphQLを使ってみることが理解への最短コースだと思うのでまず触ってみてください。
GraphQLを使い始めるにはフロントエンドとサーバーサイドの双方の理解が必要
GraphQLの生い立ちがフロントエンドとサーバーサイドの負担の軽減であるため、フロントエンドとサーバーサイドが必ずステークホルダーとして存在します。そのためGraphQLフロントエンドだけが必要としている、またはサーバーサイドだけが必要としている場合に導入することが出来ません。将来的にメリットがあることを双方が理解しない限り採用が出来ないのがこのアプリケーションの難しいところです。
まとめ
GraphQLの要素分解とGraphQLを利用した直近2~3年くらいを考察してみました。GraphQLにはWebのベストプラクティスとなる要素が多分に含まれてるにも関わらず、採用が進まない理由は個人的にはGraphQLがフロントエンドから見たユースケースの知見共有が大半で、サーバーサイドからみたGraphQLの視点が少ないからなのかなぁと思いっています。今後GraphQLがますます利用されていくことを期待して、サーバーサイド視点からのGraphQLを定期的に発信していこうかなぁと思ったりしてます。フロントエンドとサーバーサイド仲良くしていきたいですね。
所属会社: 株式会社EBILAB
所属している株式会社EBILABではサービス業・飲食業向けのデータ統合・BI・機械学習を提供するサービス, TOUCHPOINTBI, を開発しています。EBILABではデータ基盤とデータ分析部分でAzure Serverless, WebアプリケーションはGraphQL, Nuxt.js, Laravelで構築しています。開発人数4, 5人でこの規模の開発が出来ているのはAzureでのServeless, GraphQL, Nuxt.jsなどのモダンな構成にすることでWebのベストプラクティスの乗っかれているからだろうと確信しています。またGraphQLを採用することでクライアントを比較的簡単に横展開していけるのはいいですね。
ツイッターのフォローもよかったらお願いします @saboyutaka
最後に
GraphQLはいいぞ
良いお年を。
[追記 2021/07/16] [PR] Software Design 2021年8月号にGraphQLの特集を寄稿しました
Software Design 2021年8月号にて、GraphQLの特徴や解決している問題、そしてGraphQLの基本動作原理(クエリ言語、型システム、実行エンジン)について29ページに渡って特集を寄稿しました。 GraphQLを理解するための手がかりになればと思って書いたので興味ある方はぜひ読んでみてください!