Swaggerのファイル仕様が不満だったのでRAMLを触って比較してみる
前回、こういうの書きました。
mao-instantlife.hatenablog.com
で、最後に触れたSwaggerの定義ファイルへの不満点。これに悶々としていたところ、隣の席の人にRAMLというのを教えてもらったので比較がてら使ってみました。
RAML is 何?
系統としてはSwaggerと同じく、yaml形式の仕様ファイルから公開向けドキュメントやライブラリ、サーバスタブなどを作るための定義仕様です。仕様のみの提供で、開発環境やコード生成ツールなどはサードパーティやコミュニティで開発されています。
例えばライブラリ生成なんかはこれ。
API Gatewayにデプロイしたいなーというときは、AWSが公式に出しているAPIGateway Importerを使いましょう。
開発環境
デファクトになりそうなのが、API WorkbenchとAPI Designerでした。以下、両者の概要を載せておきます。私はAPI Workbenchを使っています。
API Workbench
一応0.8にも1.0にも対応を謳っていて、Swaggerの公式エディタやAPI Designerと違ってローカルのgitとも連携しやすいのが楽です。
Getting Startedで一通りRAMLの仕様を通り抜けられるところは好感高いです。ただし、まだβ版なので結構バギー。Getting Startedの通りにやろうとするとエラーになる(主にツールの操作で、ramlファイルの編集をすればいいので回避策はある)レベルの完成度。不安定ではありますが、提供されている機能的にも充実しているので使いながら待っていても十分なレベル。
API Designer
Swaggerのエディタと同じくWebベースのデザイナ、エディタです。保存はブラウザのLocal Databaseになります。RAMLはファイルの分割、インクルードができるので作ったらzipでダウンロードしてソース管理に、というワークフローになってちょっと辛いです。また、公式のdockerイメージもないので、その辺は改善して欲しいところ。
Swaggerと比較して嬉しいところ
で、数時間ほどAPI WorkbenchのGetting Startedを通しでやってみながら確認していたわけですが、Swaggerよりも嬉しいファイル仕様がいくつかありました。個人的にはこれがあるだけですごく嬉しい。どれも定義ファイルの記述を短く、シンプルにしてくれるものだと感じました。
リソースタイプ
リソース(URL)ごとにメソッドのパターンをまとめられる仕様です。inputとoutputは型パラメータのように抽象化して指定することが可能なので、再利用できます。だいたいAPIの構成ってリソースの種類ごとに似通う(in/outの型以外)よね、という発想はちゃんと仕様にしておいてくれて嬉しい限り。
resourceTypes: Collection: get: reponses: 200: body: applicaton/json: type: <<item>>[] post: body: application/json: type: <<item>>
上記は GET でリソースのリストを返し、POST で新規作成する、 item
を型パラメータとして持つリソースのパターンを示しています。これをあるリソースに適用するときはこんな感じです。
/newResource # これがURLになる箇所 type: { Collection: {item: newResourceType} } # itemに実際の型定義を指定
トレイト
トレイトはリソース単位ではなくHTTPメソッドの振る舞い(パラメータのとり方とレスポンスの返し方、型)を抽象化することができる機能です。例えば、特定の項目に対するフィルタリングの振る舞いを複数のリソースの同じメソッドで実行する場合、以下のようなトレイトを用意します。
traits: FilterableByColumnKeyword: queryParameter: include: type: string exclude: type: string
これで、特定のキーワードが含まれる場合と含まれない場合のメソッドの振る舞いを定義しました。キーワードはクエリパラメータで指定する形式です。これをあるリソースのGETメソッドに定義する場合は以下です。
/newResource: get: is: [FilterableByColumnKeyword]
サブタイプ
型定義をシンプルにするためにサブタイプの定義仕様もあります。
例えば、Supertypeという基本的な型があって、その派生型で一部属性を共有するSubtypeがあるというケース。
type: Supertype: properties: id: string name: string Subtype: type: Supertype properties: company: string
のような定義で、Supertypeの定義に追加するような派生型が作れます。型のテンプレート的な用途で使うよりは、ちゃんとモデリングして派生の関係を作ってから定義した方がいいと思います。
uses定義
in/outの型やリソースタイプ、トレイトなどを別ファイルに切り出して定義して、それを読み込む機能です。Swaggerの言語仕様にこれが見つからなくてファイルがだだ長くなって「え〜」てな感じだったので、私にとってはこれだけでもRAML圧勝です。
uses: namespace: other_library.raml
のような形式で記述します。
結局やりたいことって
これから先、デプロイをサービス単位にAPI Gateway + LambdaみたいなServerlessにしていくのはデファクトになっていくのかなあ、と思っているのですが、開発者で共有するドキュメントは欲しいです。それに、サービスだけではなく、アプリケーションとして取りまとめるためにはライブラリも必要です。そのために境界はRAMLやSwaggerのようにそれらの生成を前提とした仕様定義を行って、サービスの実装はそれを元にServerless frameworkで行う、みたいな役割分担になってくるのかなあ、と思います。Serverless SPAみたいな構成だとそもそもServerless frameworkだけでいいじゃん、的な結論になると思いますが。
次は、Serverless frameworkを少し触ってからRAMLと繋いでみようかと。
ところで、サービスごとに分割して複数サービスを束ねてアプリケーション作るようなアーキテクチャにすると、トランザクションとか大変そうなんですが、みなさんどうしてるんでしょうね。後、DDDでいうサービス層の一部がフロントエンドでの実装に変わってしまうのではないかとも思ってて、この辺もみなさんどうしてるのか気になりますね。