課題意識
クライアントとバックエンドを分離したアプリケーションを構築する時に、APIの設計で迷うことがあります。
一般的に公開しているAPIは汎用的に作られていて、例えばユーザー情報であればget users で id, name, updated_at, .... と全てのキーが取れます。
しかし、実際にクライアントが利用するキーはそのうちの一部です。
ブラウザからのアクセスを前提にするサービスの場合、不必要にデータを公開するとで情報が漏れてしまうきっかけになってしまはないだろうか?
自分でAPIを作る場合、どのようなAPIを作るべきだろうか?
このような課題について僕なりに考えてみたので記述します。
選択肢
APIを汎用的にするか、APIを特定の用途のために作成するかを考えることになります。
トレードオフ
1.汎用的に多くのキーを公開した場合
メリット
APIが汎用的になる。
デメリット
クライアント側で実際に利用する形式にデータを加工する必要がある。
不必要に余計なデータを見せてしまう事になる。
2.APIをクライアントが利用するデータ形式に加工する場合
メリット
クライアント側のデータ処理のロジックを最小限に抑えられる。
デメリット
サーバー側のAPIの汎用性が下がりロジックが複雑化する
ではどうするか?
TwitterやSlackのようなAPIからの利用を前提とするアプリ
APIは可能な限り汎用的に設計し、サーバー側にアグリゲーションやサマリなどの処理はあまり記述しなくて良いでしょう。この場合はあまり問題になりません。
SPAなどCLientアプリからのAPI利用が前提となる場合
このケースが問題になります。
TwitterやSlackなどの便利なAPIのイメージとは違い、データを公開しすぎないようにAPI毎に特定のデータを返す事になります。
なので、クライアント側とサーバー側の両方にデータ処理のロジックを持つ事になります。
ここをうまく処理するとサーバー側のロジックの汎用性を高めつつView側の処理を最低限に抑えられます。
以下、どのようなロジックをどの場所で処理すべきかに関して記述します。
Modelに相当するレイヤーに定義すべきロジック
データ構造の定義はModelに持つ事になります。
ここは特に問題ないでしょう。
コントローラーに相当するレイヤーに定義すべきロジック
データから計算を行うなどAPI特有のロジックはここに入れる事になるかと思います。
例えば、フォロワーとフォローしているユーザーの差がx人以上離れているユーザーの取得というようなケースです。
このようなデータ加工のロジックはAPIの利用を直接的に強く意識しておりコントローラーに入る事になるかと思います。
ユーザー全体のサマリの計算などもコントローラーに持つ事になるでしょう。
コントローラーとモデルの間のレイヤーに定義すべきロジック
データ取得の範囲を限定するロジックがここになるかと思います。
例えば、ユーザー全体のうち18歳以上かつ男性のユーザーを取得するというようなケース。
このような処理はデータ構造の定義とデータ加工・計算の間に位置しま。ORMを利用しているとscopeの定義などで一部モデルにこの処理を埋め込むことができますが、概念的にはコントローラーとモデルの間のレイヤーの処理となると思います。
このレイヤーを意識することで、複数のAPIに利用するための汎用的な処理を切り出すことができます。
クライアント側に相当するレイヤーに定義すべきロジック
並べ替えの処理などがここに入ります。
クエリでソートをかけてデータを取得できるのでそちらで並べ替えたくなるかもしれませんが、降順昇順などでデータの並びが動的にViewによって変えられます。
なので、データの範囲も値も変わらないような処理はクライアントに持ちましょう。
また、個別データとサマリデータの両方を表示する、といった場合は動的にサマリが変更するのでサマリ計算はクライアント側で行った方が良い場合が多いでしょう。
まとめると
- データ構造の定義はモデルに
- データの加工・計算はコントローラーに
- データの範囲のロジックはモデルとコントローラーの間に(ここが意識されずらく重要なPoint)
- データの並べ替えや一部サマリ計算はコントローラーに
おさらい
■APIからのサービス利用を前提とする場合は汎用的に
■SPAなどのクライアントアプリからのアクセスを前提とする場合はAPIは限定的にアクセスさせ、処理を汎用化するにはサーバー側のロジックを以下を意識して整理する。
- データ構造の定義はモデルに
- データの加工・計算はコントローラーに
- データの範囲のロジックはモデルとコントローラーの間に(ここが意識されずらく重要なPoint)
- データの並べ替えや一部サマリ計算はコントローラーに