2019年1月31日

「スキーマファースト」GraphQLサーバー開発の問題点

GraphQLサーバー開発のツールは、過去2年間で爆発的に増加しました。私たちは、ほとんどのツールが必要となるのは、一般的なスキーマファーストのアプローチに起因しており、別の方法、つまりコードファーストによって解決できると考えています。

The Problems of "Schema-First" GraphQL Server Development
パート1
 
(現在閲覧中)
「スキーマファースト」GraphQLサーバー開発の問題点
パート 2
 
GraphQL Nexusの紹介:コードファーストGraphQLサーバー開発
パート 3
 
GraphQL Nexusとデータベースの使用

概要:スキーマファーストからコードファーストへ

この記事では、現在のGraphQLサーバー開発の状況の概要を説明します。以下は、取り上げる内容の簡単な概要です。

  1. この記事における「スキーマファースト」とは何を意味するのか?
  2. GraphQLサーバー開発の進化
  3. SDLファースト開発の問題点の分析
  4. 結論:SDLファーストは潜在的に機能する可能性があるが、多数のツールが必要
  5. コードファースト:GraphQLサーバー開発のための言語イディオム的な方法

この記事では主にJavaScriptエコシステムの例を挙げていますが、その多くは他の言語エコシステムにおけるGraphQLサーバー開発にも当てはまります。


この記事における「スキーマファースト」とは何を意味するのか?

スキーマファーストという用語は非常に曖昧であり、一般的には非常にポジティブな考え方を伝えています。それは、開発プロセスにおいてスキーマ設計を優先することです。

実装する前にスキーマ(したがってAPI)について考えることは、通常、より良いAPI設計につながります。スキーマ設計が不十分な場合、バックエンドの実装方法の結果としてAPIが完成し、ビジネスドメインのプリミティブやAPIコンシューマーのニーズが無視されるリスクがあります。

この記事では、GraphQLスキーマがSDLで手動で最初に定義され、リゾルバーが後で実装される開発プロセスの欠点について議論します。この方法論では、SDLがAPIの信頼できる唯一の情報源です。スキーマファースト設計とこの特定の実装アプローチの区別を明確にするために、以降ではこれをSDLファーストと呼びます。

対照的に、コードファーストリゾルバーファーストとも呼ばれることがあります)は、GraphQLスキーマがプログラムで実装され、スキーマのSDLバージョンがその生成された成果物であるプロセスです。コードファーストでも、事前のスキーマ設計に多くの注意を払うことができます!


GraphQLサーバー開発の進化

The evolution of GraphQL server development

フェーズ1:graphql-jsとの初期

GraphQLが2015年にリリースされたとき、ツールエコシステムは乏しかったです。公式の仕様と、JavaScriptでのリファレンス実装であるgraphql-jsしかありませんでした。今日まで、graphql-jsは、apollo-serverexpress-graphqlgraphql-yogaなどの最も人気のあるGraphQLサーバーで使用されています。

graphql-jsを使用してGraphQLサーバーを構築する場合、GraphQLスキーマはプレーンなJavaScriptオブジェクトとして定義されます。

これらの例からわかるように、graphql-jsでGraphQLスキーマを作成するためのAPIは非常に冗長です。スキーマのSDL表現は、はるかに簡潔で理解しやすいです。

graphql-jsを使用したGraphQLスキーマの構築の詳細については、この記事をご覧ください。

フェーズ2:graphql-toolsによって普及したスキーマファースト

開発を容易にし、実際のAPI定義への可視性を高めるために、Apolloは2016年3月にgraphql-toolsライブラリの構築を開始しました(こちらが最初のコミットです)。

目標は、スキーマの定義を実際の実装から分離することであり、これが現在普及しているスキーマ駆動またはスキーマファースト / SDLファーストの開発プロセスにつながりました。

  1. GraphQL SDLでGraphQLスキーマ定義を手動で記述する
  2. 必要なリゾルバー関数を実装する

このアプローチにより、上記の例は次のようになります。

これらのコードスニペットは、graphql-jsを使用する上記のコードと100%同等ですが、はるかに読みやすく、理解しやすいです。

読みやすさは、SDLファーストの唯一の利点ではありません。

  • このアプローチは理解しやすく迅速に物事を構築するのに最適です。
  • すべての新しいAPI操作は最初にスキーマ定義に現れる必要があるため、GraphQLスキーマ設計は後付けではありません
  • スキーマ定義はAPIドキュメントとして機能できます。
  • スキーマ定義は、フロントエンドチームとバックエンドチーム間のコミュニケーションツールとして機能できます。フロントエンド開発者は権限を与えられ、API設計により深く関与するようになります。
  • スキーマ定義により、APIの迅速なモックが可能になります。

フェーズ3:SDLファーストを「修正」するための新しいツールの開発

SDLファーストには多くの利点がありますが、過去2年間で、大規模プロジェクトにスケールすることが難しいことが示されています。より複雑な環境で発生する多くの問題があります(これらについては次のセクションで詳しく説明します)。

問題自体は、実際にはほとんどが解決可能です。実際の問題は、それらを解決するには、多くの追加ツールを使用(および学習)する必要があることです。過去2年間で、エディタープラグインからCLI、言語ライブラリまで、SDLファースト開発のワークフローを改善しようとする無数のツールがリリースされています。

これらのツールをすべて学習、管理、統合するオーバーヘッドは、開発者の速度を低下させ、GraphQLエコシステムに追いつくことを困難にします。


SDLファースト開発の問題点の分析

SDLファースト開発に関する問題領域について少し深く掘り下げてみましょう。これらの問題のほとんどは、特に現在のJavaScriptエコシステムに当てはまることに注意してください。

問題1:スキーマ定義とリゾルバー間の不整合

SDLファーストでは、スキーマ定義はリゾルバー実装の正確な構造と一致する必要があります。これは、開発者がスキーマ定義が常にリゾルバーと同期していることを保証する必要があることを意味します!

これは小さなスキーマでもすでに課題ですが、スキーマが数百行または数千行に増えると、事実上不可能になります(参考までに、GitHub GraphQLスキーマは1万行以上あります)。

ツール/ソリューション:スキーマ定義とリゾルバーを同期させるのに役立つツールがいくつかあります。たとえば、graphqlgengraphql-code-generatorなどのライブラリを使用したコード生成などです。

問題2:GraphQLスキーマのモジュール化

大規模なGraphQLスキーマを作成する場合、通常、すべてのGraphQL型定義を同じファイルに配置したくありません。代わりに、それらをより小さな部分(たとえば、機能または製品に応じて)に分割したいと考えます。



ツール/ソリューション:graphql-importや、より最近のgraphql-modulesライブラリのようなツールがこれを支援します。graphql-importは、SDLコメントとして記述されたカスタムインポート構文を使用します。graphql-modulesは、GraphQLサーバーのスキーマ分離リゾルバー合成、およびスケーラブルな構造の実装を支援するツールセットです。

問題3:スキーマ定義の冗長性(コードの再利用)

もう1つの疑問は、SDL定義をどのように再利用するかです。この問題の一般的な例は、Relayスタイルのコネクションです。ページネーションを実装するための強力なアプローチを提供しますが、多くのボイラープレートと繰り返しコードが必要です。

現在、この問題を解決するツールはありません。開発者は、繰り返しコードの必要性を減らすためにカスタムツールを作成できますが、現時点では問題に対する一般的な解決策がありません。

問題4:IDEサポートと開発者体験

GraphQLスキーマは、コードの静的分析を可能にするため、開発中に非常に役立つ強力な型システムに基づいています。残念ながら、SDLは通常、プログラムでプレーンな文字列として表現されるため、ツールはSDL内の構造を認識しません。

それでは、SDLコードのオートコンプリートやビルド時のエラーチェックなどの機能を利用するために、エディターワークフローでGraphQL型を活用する方法が問題になります。

ツール/ソリューション:graphql-tagライブラリは、GraphQL文字列をASTに変換し、それによって静的分析とそれに続く機能を有効にするgql関数を公開します。それとは別に、VS Code用のGraphQLApollo GraphQLプラグインなど、さまざまなエディタープラグインがあります。

問題5:GraphQLスキーマの合成

スキーマをモジュール化するという考え方は、別の疑問につながります。既存の(および分散された)多数のスキーマを単一のスキーマに合成する方法です。

ツール/ソリューション:スキーマ合成の最も一般的なアプローチは、前述のgraphql-toolsライブラリの一部でもあるスキーマステッチングです。スキーマが正確にどのように合成されるかをより詳細に制御するために、スキーマ委譲(スキーマステッチングのサブセット)を直接使用することもできます。

結論:SDLファーストは潜在的に機能する可能性があるが、多数のツールが必要

問題領域と、それらを解決するために開発されたさまざまなツールを調査した後、SDLファースト開発は最終的には機能する可能性があるように思われますが、開発者が多数の追加ツールを学習して使用する必要があることも示唆しています。



回避策、回避策、回避策、...

Prismaでは、GraphQLエコシステムを前進させる上で主要な役割を果たしました。言及されているツールの多くは、当社のエンジニアとコミュニティメンバーによって構築されています。

Workarounds cartoon

数か月の開発とGraphQLコミュニティとの緊密な連携の後、私たちは症状を修正しているにすぎないことに気づきました。それは、ヒュドラと戦っているようなものです。1つの問題を解決すると、いくつかの新しい問題が発生します。

エコシステムロックイン:ツールチェーン全体に組み込まれること

SDLファースト開発に関する開発ワークフローの改善に常に取り組んでいるApolloの友人たちの活動に本当に感謝しています。

SDLファースト方式でGraphQLサーバーを構築するもう1つの一般的な例は、AWS AppSyncです。リゾルバーは(通常)プログラムで実装されるのではなく、スキーマ定義から自動生成されるため、Apolloモデルとは少し異なります。

コミュニティは非常に多くのツールから大きな恩恵を受けていますが、特定の組織のツールチェーンに全面的に賭ける必要がある場合、開発者にとってエコシステムロックインのリスクがあります。真の解決策は、おそらくSDLファーストの意見の多くをGraphQLコア自体に組み込むことでしょう。これは、近い将来には起こりそうにありません。

SDLファーストはプログラミング言語の個々の特性を無視する

SDLファーストのもう1つの問題点は、どのプログラミング言語を使用しても、同様の原則を課すことによって、プログラミング言語の個々の機能を無視することです。

コードファーストのアプローチは、他の言語で非常にうまく機能します。Scalaライブラリのsangria-graphqlは、Scalaの強力な型システムを活用してGraphQLスキーマをエレガントに構築し、graphlq-rubyはRuby言語の多くの優れたDSL機能を使用します。


コードファースト:GraphQLサーバー開発のための言語イディオム的な方法

必要なツールはプログラミング言語だけ

SDLファーストの問題のほとんどは、手動で記述されたSDLスキーマをプログラミング言語にマッピングする必要があるという事実に起因しています。このマッピングが、追加のツールが必要になる原因です。SDLファーストパスに従う場合、必要なツールはすべての言語エコシステムで再発明する必要があり、それぞれで異なって見えるようになります。

より多くのツールでGraphQLサーバー開発の複雑さを増すのではなく、よりシンプルな開発モデルを目指すべきです。理想的には、開発者がすでに使用しているプログラミング言語を活用できるモデルです。これがコードファーストのアイデアです。

コードファーストとは正確には何か?

graphql-jsでスキーマを定義する最初の例を思い出してください。これがコードファーストの意味の本質です。スキーマ定義の手動で管理されたバージョンはなく、代わりにSDLはスキーマを実装するコードから生成されます。

graphql-jsのAPIは非常に冗長ですが、すでに述べたgraphlq-ruby sangria-graphql、およびPython用のgrapheneやElixir用のabsinthe-graphqlなど、コードファーストのアプローチに基づいて動作する他の言語の多くの人気のあるフレームワークがあります。



実践的なコードファースト

この記事は主にSDLファーストの問題を理解することに関するものですが、コードファーストフレームワークでGraphQLスキーマを構築することがどのようなものかについての小さな予告編を次に示します。


このアプローチでは、GraphQL型をTypeScript/JavaScriptで直接定義します。適切な設定とインテリジェントコード補完のおかげで、エディターは利用可能なGraphQL型、フィールド、および引数を定義時に提案できます。

一般的なエディターワークフローには、ファイルが保存されるたびに型定義を再生成するバックグラウンドで実行されている開発サーバーが含まれます。

すべてのGraphQL型が定義されると、それらはGraphQLSchemaインスタンスを作成するための関数に渡され、GraphQLサーバーで使用できます。ouputsを指定することで、生成されたSDLと型定義の場所を定義できます。

この記事シリーズの次のパートでは、コードファースト開発についてさらに詳しく説明します。

すべてのツールを必要とせずにSDLファーストの利点を享受する

先ほど、SDLファースト開発の利点を列挙しました。実際、コードファーストのアプローチを使用する場合、それらのほとんどを妥協する必要はありません。

GraphQLスキーマをフロントエンドチームとバックエンドチームの重要なコミュニケーションツールとして使用することの最も重要な利点は変わりません。

例としてGitHub GraphQL APIを見てみましょう。GitHubはRubyとコードファーストのアプローチを使用してAPIを実装しています。SDLスキーマ定義は、APIを実装するコードに基づいて生成されます。ただし、スキーマ定義はバージョン管理にチェックインされています。これにより、開発プロセス中にAPIへの変更を非常に簡単に追跡でき、さまざまなチーム間のコミュニケーションが向上します。

APIドキュメントやフロントエンド開発者のエンパワーメントなどの他の利点も、コードファーストのアプローチで失われることはありません。

コードファーストフレームワーク、近日IDEに登場

この記事はかなり理論的であり、多くのコードを含んでいませんでしたが、コードファースト開発に興味を持っていただけたなら幸いです。さらに実践的な例を見て、コードファースト開発の経験について詳しく学ぶには、乞うご期待ください。Prisma Twitterアカウントを今後数日間監視してください 👀

この記事についてどう思いますか? Prisma Slackに参加して、GraphQL愛好家仲間とSDLファーストおよびコードファースト開発について話し合いましょう。


🙏 記事に関するフィードバックをくれたSashkoApolloチームに多大な感謝を申し上げます!

次回の投稿をお見逃しなく!

Prismaニュースレターにサインアップ