Kaizen Platformでフロントエンド開発をやっているlacoです。
新規アプリケーション開発において、API仕様中心の開発スタイルを検討し、実験的に取り入れました。 本記事ではその概要と効果を紹介します。
API仕様中心開発
API仕様中心開発を取り入れようと思ったきっかけは、2017年のNode学園祭でpika_shiさんが発表した「JSON Schema Centralized Design」です。
JSON Schema Centralized Design // Speaker Deck
Kaizen Platformではリモートワークで開発しているメンバーが多く、非同期にコミュニケーションをすることが多いので、生産性を高めるためには互いの作業を待たずに独立して分業できるワークフローが必要でした。 バックエンドAPIの実装を待たないとフロントエンドが実装できないような依存関係は避けなければなりません。
そこで、フロントエンドとバックエンドが分業を始める前に先んじてAPI仕様を定義し、そのAPI仕様を中心に分業を行おうと考えました。バックエンドはAPI仕様を満たすようなAPIを実装し、フロントエンドはAPI仕様に準拠したレスポンスが得られることを前提に実装します。これをKaizen PlatformにおけるAPI仕様中心開発と定義づけました。
API仕様中心開発において達成したい目的は次の2つです。
- フロントエンド/バックエンドがスムーズに協働・分業するための Living なAPI仕様を定義すること
- 仕様と実装が乖離しないAPIドキュメンテーションを維持すること
これらを実現するために、次のものを用意する必要がありました。
- API仕様を明確に定義するためのフォーマット
- 記述したAPI仕様を管理・閲覧できる場
- 記述したAPI仕様をフロントエンド/バックエンドの実装から参照する仕組み
GraphQLによるAPI仕様定義
今回のAPI仕様中心開発においては、API仕様を定義するフォーマットとしてGraphQL Schemaを採用しました。 その理由は次のとおりです。
- モデル定義とエンドポイント定義が書きやすく読みやすい言語であったため
- GraphQLスキーマからのドキュメントHTML生成が容易であった
- 実装もGraphQLにすることで、コード生成やバリデーションなど、API仕様を実装から参照する仕組みが構築しやすかった
このテーマについては、GraphQL Meetup Tokyo #5で議題に挙げさせていただいて、有意義な議論ができました。
書きやすく読みやすい言語
現状、API仕様を定義するための言語・フォーマットとしてはOpen API Spec (Swagger)などの選択肢もありましたが、次のような理由でGraphQL Schemaを採用しました。
- SwaggerのJSONは複雑で手書きしにくい
- TypeScriptの型定義ファイルだけではモデルの定義しかできない
- GraphQLのスキーマ言語はAPIサーバーのための専用言語なので、ユースケースがマッチしている
今回の開発手法においては、API仕様はフロントエンドとバックエンドが実装前に検討して定義するものなので、手書きしやすさを重視した選定となっています。
(GraphQL Schemaの例)
ドキュメントHTML生成
GraphQL Schemaはgraphdocのようなツールで簡単にドキュメンテーション可能でした。 CIでドキュメンテーション生成を自動化できたため、メンテナンスするのは手書きのGraphQL Schemaファイルだけでした。
(自動生成されるAPIドキュメンテーションの一部)
参考:
API仕様を実装から参照する仕組み
GraphQLを実装にも使うことで、実装がAPI仕様に準拠していることを機械的に保証することができました。 バックエンドでは実装から生成されたGraphQL Schemaとの差分をチェックし、フロントエンドではGraphQL Schemaから生成したTypeScript型定義ファイルを利用して型の整合性を担保しました。
結果として、GraphQL SchemaをAPI仕様のフォーマットとして採用したアプリケーションの構成は、次のような形になりました。
この開発手法において、API仕様のフォーマットの重要なポイントは次の2つです。
- 書きやすさ
- メンテナンスが面倒にならないこと
- チームメンバーが誰でもメンテナンスできること
- 読みやすさ
- あいまいさがないこと
- チームメンバーが誰でもAPI仕様を理解できること
バックエンドがAPI仕様の更新をスキップして実装しはじめた瞬間に崩壊するので、 API仕様中心開発の成功はバックエンドのメンバーの貢献にかかっているといっても過言ではありません。 そのため、API仕様が更新しやすいこと、面倒くさくならないことが極めて重要です。
開発フローについて
API仕様中心開発の具体的な進め方は、次のような流れになりました。
- 担当(誰でも良い)が要件を満たすためのAPI仕様の変更プルリクエストを投げる
- チームメンバーでレビューし、合意が取れたところでmergeする
- merge後にフロントエンド、バックエンドが実装をおこない、実装が揃ったらリリース可能となる
このワークフローを実行したなかで、次の点への注意が必要だとわかりました。
API仕様のバグはそこそこ発生する
仕様に合意したものの、実際に実装しはじめるとAPI仕様に問題があったり、よりよい仕様があることがわかったりするケースが少なくありませんでした。 原因はGraphQLへの理解の不足や、アプリケーションのユースケース定義の漏れなど様々でした。 そうした場合も、その変更をふたたびAPI仕様リポジトリに提案し、合意をもって修正しました。
ここで重要なのは、こまめな(できれば毎日)コミュニケーションで、早くお互いの不満や問題意識を共有することです。分担後の互いの実装状況や困っていることを共有することで、API仕様の問題に早く気づくことができました。
現在の実装が準拠しているスキーマの版をわかるようにしておく
dev環境やQA環境、本番環境で、そのデプロイのバージョンが準拠している仕様のリビジョンは追えるようにしておく必要があります。 互換性のないAPI仕様の変更があったときには、フロントエンドとバックエンドのデプロイの時差によるダウンタイムについて考慮が必要でした。
API仕様中心開発の効果
ここまで紹介したKaizen PlatformのAPI仕様中心開発を実際に導入し、次のような効果がありました。
恩恵
- バックエンド実装にブロッキングされずフロントエンドを実装できた
当初の目的どおり、バックエンドのAPI実装を待たずにフロントエンドの実装を進められました。 リクエストとレスポンスの仕様が決まっているため、正しいモックを実装しやすく、バックエンドAPIが実装できたあとにはモックから差し替えるだけでほとんどリリース可能な状態になっていました。
- チーム内でAPIについてのコミュニケーションが楽だった
フロントエンドにもバックエンドにも依存しない独立したAPI仕様が共通言語として機能したため、 リクエストやレスポンスの型についてのコミュニケーションが円滑に進みました。
- GraphQL Schemaからの型生成により、型安全なGraphQLクライアント実装ができた
apollo-codegenを使って生成したTypeScript型定義ファイルのおかげで、型安全なGraphQLクライアント実装ができました。
実際にリクエストするクエリに対応したレスポンス型が生成されるため、フィールドの有無や型について悩むことがなくなりました。
GraphQLのNullableの情報もTypeScriptに変換されるため、strictNullChecks
の恩恵も受けられたのが非常に良かったです。
課題
- API仕様にバグ(考慮漏れ)があると両方に影響する
API仕様中心開発はバックエンドとフロントエンド間での依存関係はないものの、双方がAPI仕様に依存します。 そのため、先述のとおりAPI仕様にバグがあると両方の実装に影響してしまいます。 なるべく手戻りを避けるためには、API仕様をバリデーションするような仕組み、API仕様のユニットテストなども視野にいれる必要があるかもしれません。
- API仕様に準拠してフロントエンドを書いてもバックエンドに漏れがあると壊れる
当然ですが、API仕様中心開発はフロントエンドとバックエンドの双方が仕様に準拠して実装しているという約束の下に分業を効率化しています。 そのためバックエンドの実装がAPI仕様からずれているとフロントエンドとしてはどちらに合わせるべきか悩むことになってしまいます。 我々のチームでは、サーバー実装がAPI仕様に準拠していることをチェックする仕組みを作って解決しましたが、将来的にはバックエンドもGraphQL Schemaから生成されたソースコードを使うようになると良いかもしれません。
まとめ
チームのほぼ全員がAPI仕様中心開発もGraphQLも未経験でしたが、最初に目的を明確にしたことで、軸をブレさせずに試行錯誤しながら進めることができました。 チーム内でGraphQLの知見も深まりましたし、今後の開発においても採用するモチベーションが高まりました。
効率的な協働・分業のワークフローの追求のため、今後もこのAPI仕様中心開発をベースに改善を続けていこうと考えています。