2019年1月31日

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

過去2年間で、GraphQLサーバー開発のためのツールは爆発的に増加しました。これらのツールのほとんどは、一般的なスキーマファーストのアプローチから生じるニーズに対応しており、別の方法であるコードファーストによって解決できると私たちは考えています。

The Problems of "Schema-First" GraphQL Server Development
 
(現在読んでいる記事)
「スキーマファースト」なGraphQLサーバー開発の課題
 
GraphQL Nexusの紹介:コードファーストなGraphQLサーバー開発
 
データベースと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の初期

2015年にGraphQLがリリースされたとき、ツールエコシステムはまだ乏しいものでした。公式の仕様書と、そのJavaScriptでの参照実装であるgraphql-jsしかありませんでした。今日に至るまで、graphql-jsapollo-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年間で、SDLファースト開発に関するワークフローを改善しようとする無数のツールがリリースされてきました。エディタプラグインからCLI、言語ライブラリまで様々です。

これらのツールすべてを学習し、管理し、統合するオーバーヘッドは、開発者の作業を遅らせ、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:スキーマ定義の冗長性(コードの再利用)

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

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

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

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

そこで問題となるのは、SDLコードのオートコンプリートやビルド時エラーチェックなどの機能の恩恵を受けるために、エディタのワークフローでGraphQL型をどのように活用するかです。

ツール/解決策:graphql-tagライブラリは、GraphQL文字列をASTに変換するgql関数を公開しており、これにより静的解析とその後の機能が可能になります。それ以外にも、VS Code用のGraphQLApollo GraphQLプラグインなど、さまざまなエディタプラグインがあります。

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

スキーマをモジュール化するという考え方は、もう一つの疑問を投げかけます。既存の(そして分散された)複数のスキーマをどのように単一のスキーマに構成するか、ということです。

ツール/解決策:スキーマ構成の最も一般的なアプローチは、前述のgraphql-toolsライブラリの一部でもあるスキーマスティッチングです。スキーマがどのように構成されるかをより細かく制御するには、スキーマデリゲーション(スキーマスティッチングのサブセット)を直接使用することもできます。

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

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



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

Prismaでは、GraphQLエコシステムの推進に大きな役割を果たしてきました。言及されたツールの多くは、当社のエンジニアやコミュニティメンバーによって構築されてきました。

Workarounds cartoon

数ヶ月の開発とGraphQLコミュニティとの密接な交流を経て、私たちは症状を修正しているに過ぎないことに気づきました。それはまるでヒュドラとの戦いのようです。一つの問題を解決すると、新たな複数の問題が生じます。

エコシステムのロックイン:ツールチェーン全体への依存

SDLファースト開発に関する開発ワークフローの改善に常に取り組んでいるApolloの友人たちの仕事に、心から感謝しています。

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

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

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

SDLファーストのもう一つの問題点は、どのプログラミング言語が使用されても同様の原則を課すことで、プログラミング言語の個々の特性を無視していることです。

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


コードファースト:GraphQLサーバー開発のための言語に即した方法

あなたが必要とする唯一のツールは、あなたのプログラミング言語です

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

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

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

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

graphql-jsのAPIは非常に冗長ですが、すでに述べたgraphlq-rubysangria-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に参加して、SDLファーストとコードファースト開発について、GraphQL愛好家仲間と話し合いましょう。


🙏 SashkoApolloチームの記事へのフィードバックに心から感謝いたします!

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

Prismaニュースレターに登録する

© . All rights reserved.