Xでこんな投稿を見かけました。(元のポスト)
A)
GET /users/profile
GET /users/settings
GET /users/orders
B)
GET /users?type=profile
GET /users?type=settings
GET /users?type=orders
投稿では「Bの方が圧倒的にスケーラブル」と書かれていました。
しかし、私は最初A一択だと思いました。
実は「スケーラブル」の意味が違っていた
議論を読んでいて気付いたのは、お互いが考えている「スケーラブル」が違うということです。
A派が考えるスケーラビリティ
API設計としてのスケーラビリティです。
- リソースが分かりやすい
- レスポンス型が固定
- OpenAPIを書きやすい
- SDKを生成しやすい
- 保守しやすい
例えば
GET /users/profile
を見れば「Profileを取得するAPI」であることが一目で分かります。
新しい機能が増えても
GET /users/security
GET /users/preferences
GET /users/notifications
と自然に追加できます。
B派が考えるスケーラビリティ
一方で投稿者が言っていたのは、インフラ構成の話でした。
例えば
API Gateway
│
/users?type=profile
│
Profile Service
/users?type=settings
│
Settings Service
というように、入口は1つのままで内部サービスだけ増やせるという考え方です。
つまり「エンドポイントを増やさなくても済む」という意味でスケーラブルと言っていました。
でもこれはURL設計とは関係ない
ここで一番違和感がありました。
同じことは
/users/profile
/users/settings
/users/orders
でもできます。
API Gateway
│
/users/profile ─▶ Profile Service
/users/settings ─▶ Settings Service
/users/orders ─▶ Order Service
つまり、サービス分割やスケールアウトはURL設計とは独立した話です。
Aだからスケールできない、Bだからスケールするという話ではありません。
もう一つ気になったこと
Bの設計を突き詰めると、
GET /users?type=profile
だけでは済まなくなります。
そのうち
GET /users?type=profile,settings
や
GET /users?type=profile&fields=name,email
のようになっていきます。
これは「何を返すか」をクエリパラメータで指定する設計です。
ここまで来ると、RESTというよりGraphQLの発想に近づいていきます。
GraphQLは
query {
me {
profile {
name
}
settings {
theme
}
}
}
のように取得したいデータをクライアントが指定します。
つまり、「返すものをクエリで切り替える」という考え方自体がGraphQL寄りなのです。
結論
今回の議論で一番面白かったのは、AかBかではなく、「スケーラブル」という言葉の意味がお互い違っていたことでした。
- API設計としてのスケーラビリティ
- インフラ構成としてのスケーラビリティ
この2つを混ぜてしまうと議論は噛み合いません。
個人的には、REST APIとして設計するなら、リソースごとに責務とレスポンスを明確に分けるAの方が自然だと感じています。