Open Api Specification(Swagger)ファイルを分割する方法


問題

Open Api Specificationファイルを書き進めると中規模程度のサーバですら仕様ファイルが1000行を簡単に超える。
しかも1つのAPIについての記述がpaths下、requestbody下、schema下など非常に分散するので編集が辛い。

既存の解決策

swglowの分割手法は非常に良い気がしたが、現状ではOAS 3.0のファイルには対応していなかった。
コードを見ていると、swglowではswaggerの構造に特化した処理をしているがそもそも単純にyamlファイルをマージする感じでよいのではないかと思った。
なので自分で作ることにした。

解決策

1. Open Api Specificationファイルをyamlとして書いた上でAPI種別ごとに分割する。

petstore APIで例えるなら以下のように分割する感じ。

  • common.yml
  • pet.yml
    • petタグが付けられたpathsオブジェクトやcomponents/requestBodies, components/schema などを集約する
  • store.yml
    • storeタグが付けられたpathsオブジェクトやcomponents/requestBodies, components/schema などを集約する
  • user.yml
    • petタグが付けられたpathsオブジェクトやcomponents/requestBodies, components/schema などを集約する

2. yaml_strict_mergeツールを使って分割したyamlを合成(merge)する

rubyが動く環境ならgemで直接実行しても良いが、環境への依存を最小限にしたければkbigwheel/yaml_strict_merge - Docker Hubが使える。

この手法のメリット

1. 分割方法が非常に柔軟

単純にyamlのツリーをすべてマージするだけなので、分割方法の自由度が非常に高くAPI設計に合わせて分割できる。

2. 分割したファイルもOpen Api Specificationファイルとして正当にできる

yaml_strict_mergeは単純にすべてのファイルを合成する。
ということは、分割したpet.ymlやstore.ymlでもトップレベルでopenapiフィールドやinfoフィールドなどの必須プロパティを
きちんと書けば分割したファイルもまた正規のOASファイルとして扱うことができる。
それによりJSON schemaによるvalidationやSwagger-editorなどによる編集支援もエラーを無視する必要なく十全に使用できる。

3. API仕様のズレ・衝突の早期検知

API仕様ファイルを分割した場合、お互いのファイルで矛盾した記述が行われる可能性がある。
例えば2つのファイルでたまたま同じスキーマ名を付けてしまった、あるいはOASバージョン指定が3.0.0と3.0.1でずれた場合などだ。
この問題は非常にやっかいで、もしそれに気づけなかった場合、合成結果がどちらになるかは合成方法に依存し、非常に気づきづらい潜在的なバグとなる。

私が調べた限りjsonやyamlのメジャーなmergeツールのすべてはこの矛盾を無視してどちらかの値を無条件で選択する。
しかしyaml_strict_mergeはこの値の矛盾を許容しない。このツールは同じオブジェクトの同じプロパティの値が矛盾した場合、エラーが出て処理が止まる。配列の長さが違った場合や配列の要素が異なる場合も同様である。
これによりOASファイルを分割しても、早期に定義のズレ・矛盾を検出できる。

蛇足

yamlではアンカーとエイリアスを使うことでよりDRYに記述できるが、swagger仕様的にはこれを許容していない。
swagger-uiは概ね正常に描画してくれるがたまに表示されないときもある。
そこでbigwheel/yaml_expand_aliasを使えばalias先でanchorの値を展開しつつすべてのalias/anchorを取り除ける。

この2つを組み合わせて、分割したファイルでは特定リソースに集中したAPI定義を集めた上で可能限りalias/anchorを使いDRYにしつつ、
合成した先のファイルはanchor/aliasがなくクリーンですべてのAPI情報が集約されたものにするというのが2018/09時点での僕のベストソリューション。