Oisix ra daichi Creator's Blog(オイシックス・ラ・大地クリエイターズブログ)

オイシックス・ラ・大地株式会社のエンジニア・デザイナーが執筆している公式ブログです。

第三世代バックエンド開発の紹介 - OpenAPI Generator を活用したAPI駆動開発

本記事では、以前の記事でご紹介した第三世代アーキテクチャにて使用しているOpenAPI Specの活用事例についてご紹介いたします。 私たちは現在、マイクロサービスアーキテクチャを採用した「第三世代アーキテクチャ」の構築を進めています。 以前の記事では、バックエンドチームの開発の進め方についてご紹介しました。 マイクロサービス開発では、サービス間のAPI連携が複雑化しやすく、「API仕様の認識齟齬」「クライアントとサーバーの実装不整合」「並行開発時のコンフリクト」といった課題に直面します。これらの課題を解決するため、私たちはOpenAPI SpecとGit Submoduleを組み合わせた開発フローを構築しました。 本記事では、その設計思想と実装方法、実際に得られた改善効果についてご紹介します。

次の図は、第三世代アーキテクチャの全体構成を示しています。

第三世代アーキテクチャの構成図


第三世代アーキテクチャにおけるAPI First戦略

Beyond the Twelve-Factor Appに基づく設計思想

第三世代アーキテクチャでは、クラウドネイティブなアプリケーション開発のベストプラクティスとして知られる「Beyond the Twelve-Factor App」の考え方をベースに設計をしています。この方法論は、Herokuのエンジニアが提唱した「The Twelve-Factor App」を拡張したもので、マイクロサービスアーキテクチャにおける開発・運用のガイドラインを提供しています。

その中でも特に重要視しているのが「API First」という考え方です。API Firstとは、システム開発においてAPIの設計を最優先で行い、その設計を起点として開発を進めるアプローチです。従来の開発では、実装を進めながらAPIの仕様を決定していくことが多く、結果としてAPI仕様が実装に引きずられてしまうことがありました。

第三世代アーキテクチャでは、マイクロサービス間の通信をすべてAPIで行うため、API設計の品質がシステム全体の品質に直結します。そのため、実装に先立ってAPI仕様を確定させ、その仕様をもとに各サービスの開発を並行して進められる体制を整えています。

OpenAPI Specを Single Source of Truth とする理由

API Firstを実現するために、私たちはOpenAPI Specを「Single Source of Truth(信頼できる唯一の情報源)」として位置づけています。OpenAPI Specは、RESTful APIの仕様を記述するための標準フォーマットであり、YAML形式で人間が読みやすく、かつ機械的に処理可能な形式でAPI仕様を定義できます。

Single Source of Truthとして採用した理由は主に3つあります。

1. 契約とコードの分離

API仕様(契約)を独立したファイルとして管理することで、実装コードから仕様を切り離すことができます。これにより、API仕様の変更履歴を明確に追跡でき、仕様変更の影響範囲を把握しやすくなります。

2. API変更をプルリクエストでレビュー可能

OpenAPI Specファイルへの変更は、通常のコード変更と同様にプルリクエストを通じてレビューされます。これにより、API仕様の変更が関係者全員に共有され、破壊的変更を事前に検知できます。

3. ServerとClientの認識ずれを防止

API提供側(Server)とAPI利用側(Client)が同一のOpenAPI Specからコードを生成するため、仕様の解釈違いによる不具合を防止できます。


OpenAPI Specリポジトリの設計

リポジトリ構成

第三世代アーキテクチャでは、OpenAPI Specを専用のリポジトリで一元管理しています。 このリポジトリには、バックエンドAPIやBFF(Backend for Frontend)のAPI仕様が格納されており、次のようなディレクトリ構成となっています。

openapi-spec/
├── spec/
│   ├── bff/
│   │   ├── web/    # Web用BFFのOpenAPI Spec
│   │   └── app/    # アプリ用BFFのOpenAPI Spec
│   └── backend/
│       ├── customer/       # 顧客サービスのOpenAPI Spec
│       ├── merchandise/    # 商品サービスのOpenAPI Spec
│       └── order/          # 注文サービスのOpenAPI Spec
└── validation/             # OpenAPI Specのバリデーション設定

validationディレクトリには、OpenAPI Specの記述ルールを検証するための設定ファイルを格納しています。これにより、プルリクエスト時にAPI仕様の記述が規約に沿っているかを自動的にチェックできます。

この一元管理により、各マイクロサービスが提供するAPIの仕様を横断的に把握できます。また、新規にマイクロサービスを追加する際も、既存のAPI仕様を参照しながら設計できるため、APIの一貫性を保ちやすくなっています。

なお、第三世代アーキテクチャのバックエンドAPIは社外への公開は行わず、マイクロサービス間の内部通信専用として使用しています。外部公開を想定しないことで、認証・認可の複雑さを軽減し、開発速度を向上させています。

APIのバージョニング戦略

マイクロサービスアーキテクチャでは、各サービスが独立してデプロイされるため、APIの破壊的変更への対応が重要な課題となります。第三世代アーキテクチャでは、APIのバージョニング戦略として「URIパスによるバージョン管理」を採用しています。

リクエストパスによるバージョン管理

具体的には、APIのリクエストパスに/v1/v2のようなバージョン番号を含める方式です。

# バージョン1のAPI
GET /v1/merchandise/{merchandiseId}

# バージョン2のAPI(破壊的変更を含む)
GET /v2/merchandise/{merchandiseId}

この方式を採用した理由は、サーバー側が古いバージョンのエンドポイントを削除しない限り、クライアント側は任意のタイミングで新バージョンに移行できるためです。これにより、複数のマイクロサービスが関わる変更でも、各サービスが独立したタイミングでリリースできます。

破壊的変更が必要な場合は、新しいバージョンのエンドポイントを追加し、古いバージョンを一定期間維持します。すべてのクライアントが新バージョンに移行したことを確認してから、古いバージョンを廃止するという段階的な移行が可能です。


Git Submoduleの採用

OpenAPI Generatorによるコード生成

OpenAPI Generatorは、OpenAPI Specからサーバー側のスタブコードやクライアント側のAPIクライアントコードを自動生成するツールです。Java、TypeScript、Goなど多くの言語に対応しており、生成されるコードのライブラリ(Spring Boot、WebClientなど)も選択できます。

私たちは、OpenAPI Specから自動生成されたコードを各マイクロサービスで利用する方法として、Git Submoduleを採用しました。各マイクロサービスのリポジトリに上述したOpenAPI SpecのリポジトリをSubmoduleとして組み込み、ビルド時にOpenAPI Generatorでコードを生成する構成としています。

# .gitmodules の例
[submodule "api-spec"]
    path = api-spec
    url = git@github.com:xxxxxxxxx.git

Git Submoduleによるコード生成構成

この構成の最大のメリットは、OpenAPI Specの自動生成コードを各プロジェクト内で生成することで、他のプロジェクトへの影響を及ぼさない疎結合な構成を実現できることです。

OpenAPI Specはあくまで「契約内容」を記載するものであり、使用するプログラミング言語やライブラリは、サーバー側・クライアント側がそれぞれ自由に選定できます。例えば、あるサービスではSpring WebFluxのWebClientを使い、別のサービスではRestTemplateを使うといった選択が可能です。

Git Flowとの組み合わせによるバージョン固定

Git SubmoduleとGit Flowを組み合わせることで、一定タイミングでのOpenAPI Specバージョンを固定する仕組みを実現しています。

  • mainブランチ: 本番環境にデプロイされるコードは、OpenAPI Specのリポジトリのmainブランチを参照
  • developブランチ: 開発中のコードは、OpenAPI Specのリポジトリのdevelopブランチを参照
  • featureブランチ: 開発中のコードは、OpenAPI Specのリポジトリのdevelopブランチを参照。ただし、ローカル環境ではOpenAPI Specのリポジトリのfeatureブランチを参照することも可能。

次の図は、各ブランチの参照関係を示しています。 Git FlowとSubmoduleの参照関係

この運用により、本番環境では安定したAPI仕様を使用しつつ、開発環境では最新のAPI仕様を取り込んで開発を進められます。リリース時にSubmoduleの参照先をmainブランチに切り替えることで、本番環境のAPI仕様を確定させます。

artifactを使用しない判断

一般的に、自動生成されたコードをMaven CentralやGitHub Packagesなどのartifactリポジトリにpublishし、依存関係として取り込む方法もあります。しかし、第三世代アーキテクチャではこの方法を採用しませんでした。

その理由は、OpenAPI Specに特定のバージョンを付与してpublishする手順を踏むと、複数人での同時開発が難しくなるためです。例えば、AさんとBさんが同時に異なるAPI変更をする場合、それぞれがバージョン番号を付与してpublishするタイミングを調整する必要が生じます。

Git SubmoduleとGit Flowの組み合わせでは、ブランチを固定することで同等の安定性を担保しつつ、バージョン番号の調整という煩雑な作業を省略できます。これにより、開発者はAPI仕様の変更に集中でき、開発速度の向上につながっています。


マイクロサービスでの活用例

注文サービスの構成

注文情報を提供する注文サービスでは、openapi-generate.gradleにて次のコード生成タスクを定義しています。

// サーバーサイドコードの生成(Spring Boot)
tasks.register("generateServer", GenerateTask) {
    generatorName = "spring"
    library = "spring-boot"
    inputSpec = "$rootDir/spec/backend/order/api.yml"
    configOptions = [
        interfaceOnly: "true",  // インターフェースのみ生成
        useTags: "true",
    ]
}

// クライアントコードの生成(WebClient)
tasks.register("generateClientCustomer", GenerateTask) {
    generatorName = "java"
    library = "webclient"
    inputSpec = "$rootDir/spec/backend/customer/api.yml"
}

サーバーサイドではinterfaceOnly: "true"を指定し、Controllerのインターフェースのみを生成しています。実際のビジネスロジックは、生成されたインターフェースを実装するクラスに記述します。これにより、OpenAPI Specが変更された場合でも、ビジネスロジックへの影響を最小限に抑えられます。


実際に改善したこと

OpenAPI SpecとGit Submoduleを組み合わせた構成を導入したことで、次のような改善効果がありました。

並行開発時のコンフリクト解消

従来のartifactリポジトリ方式では、複数人で同時に開発する際にバージョン指定でコンフリクトが発生していました。これにより作業の手戻りを招くことがありました。 Git Submoduleとブランチによるバージョン管理に移行したことで、このような問題が解消されました。

取り込み漏れによるエラーの防止

CIパイプラインで各リポジトリの最新状態を自動的に取得する仕組みを構築したため、OpenAPI Specの取り込み漏れによるビルドエラーや実行時エラーが発生しなくなりました。

開発者の認知負荷の軽減

以前は「誰かが自分と同じサービスを開発しているか」を常に意識する必要がありました。現在の構成ではその必要がなくなったため、開発者は自分のタスクに集中できる環境が整いました。


まとめ

本記事では、第三世代アーキテクチャにおけるOpenAPI Specの活用事例をご紹介しました。

私たちは、Beyond the Twelve-Factor Appの「API First」の考え方に基づきOpenAPI SpecをSingle Source of Truthとして位置づけています。 Git SubmoduleとGit Flowを組み合わせることで、artifactリポジトリへのpublishを省略しつつ、安定したバージョン管理を実現しました。

この構成により、次のメリットを得られています。

  • 契約とコードの分離: API仕様の変更履歴を明確に追跡可能
  • 型安全なAPI連携: 自動生成されたコードにより、コンパイル時にAPI不整合を検知
  • 並行開発の効率化: 複数チームが同時にAPI変更しても、バージョン番号の調整が不要
  • 段階的な移行: URIパスによるバージョン管理で、破壊的変更も安全にリリース可能

この事例はあくまで弊社の環境に合わせた活用方法であり、ベストプラクティスではありません。OpenAPI Specの導入を検討されている方の参考になれば幸いです。

Oisix ra daichi Creator's Blogはオイシックス・ラ・大地株式会社のエンジニア・デザイナーが執筆している公式ブログです。

オイシックス・ラ・大地株式会社では一緒に働く仲間を募集しています