328
302

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GraphQLAdvent Calendar 2020

Day 14

GraphQLはサーバーサイド実装のベストプラクティスとなるか

Last updated at Posted at 2020-12-14

この記事は 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

apollo.png

出典: https://www.apollographql.com/docs/apollo-server/

ServelssやMicroservicesなどの設計パターンではよく利用される設計パターンと実装です。APIの数が多くなる場合や、クライアントが多くなる場合にはAPI Gateway Patternを利用することで解決することが出来ます。API Gateway Patternはサブセットとして、ゲートウェイルーティングパターンゲートウェイ集約パターンゲートウェイオフロードパターンを内包します。

gateway.png
出典: API ゲートウェイ - Azure Architecture Center

URLベースAPI GatewayのOSS実装としてKong GatewayKrakenDがあります。Kurbernetes界隈ではIstio Ingressが使われるみたいです。クラウドではAzureにはAzure Application GatewayAzure API Management, AWSにはAmazon API Gatewayがあります。

API GatewayはL7のロードバランサの特徴を引き継いで実装されています(わかりやすい例としてKong Gatewayはnginxの機能拡張として実装されています)。API Gatewayの機能としてわかりやすいのでKrakenDの機能一覧を見てみます。

KrakendFlow.png
出典: https://www.krakend.io/

  • モニタリング
    • ロギング
    • スタッツ
  • セキュリティ
    • 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や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について言及します。

apollo_connect_everything.png

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_as_onion.png
出典: Pylons のコンセプト

CleanArchitecture.jpg
出典: 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を採用したときの責務分離

EBILAB_System_2019_08_登壇用_EBILAB_System_architecture_-_Cacoo.png

GraphQLがAPI GatewayとしてHTTPSを喋り、middlewareを集積することでクリーンアーキテクチャでいうExternal InterfacesとGateways, Controllers, Presentersの層を担当。gRPCを採用したドメインロジックを受け持つサーバーはコアロジック、ServiceやUseCase, Entityにのみ注力する単純な関数として表現できるようになります。不要なコンテキストを一切持つ必要がないためHTTPフレームワークのような重厚なライブラリを必要とせず、ほとんどプログラミング言語で書くプレーンなコードだけでコーディングできるようになります。

gRPCサーバーアプリケーションはFaaSの書き味と近い

クラウドプラットフォームで利用可能なFaaSであるAzure FuncitonsAWS LambdaGCP 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を運用するための工夫

また、個人として持っている段階的アーキテクチャ変更の戦略を共有しておきます。

余談ですが、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を理解するための手がかりになればと思って書いたので興味ある方はぜひ読んでみてください!

TH320_642108.jpg

328
302
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
328
302

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?