質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

SPA開発におけるAPI設計ってどういうものが好ましいのでしょうか?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,680

score 60

質問

SPA開発におけるAPI設計なのですが、何が良いのかわかりません。
特にGETメソッドのエンドポイントに関してです。

特定のクライアント用のAPIなので、汎用的なものではなく、ある程度、クライアントを意識したもので良いという前提があると思うので、
画面で必要な情報を全て返すAPIエンドポイントを用意してあげるのが良いのでしょうか?

それとも、リソース単位のAPIをクライアントに提供して、クライアント側では必要に応じて、受け取った情報を用いて、計算、判断、加工などの処理を行ってもらうのが良いのでしょうか?

もしくは、「要はバランスだよね」おじさん的な感じになるのでしょうか!?

前提、ビジネスロジックはバック側に持たせるで良いと思うのですが、画面依存させないAPI(リソース単位のAPI)を提供しているとどうしても、フロント側でリソースAの情報の一部と、リソースBの情報の一部をそれぞれ組み合わせて、加工、判断、計算をしないといけないような場面が出てきてしまうと思っています。

例えば、ログインユーザーのロールがXYZというロールで、かつ、帳票が生成済みの場合であれば、ダウンロードボタンを表示、または、活性化させたいような場合、ユーザーリソースに含まれるロール情報と、帳票情報に含まれるステータスの両方をみて、ダウンロード可能かどうかをフロント側で判断しないといけないと思います。

EvansのDDD本にはアンチパターンとしてSmart UIなる表現がありますし、最近、ツイッター界隈ではフロントエンドはJSON色付け係に徹するべなる主張も見かけまして、SPA開発におけるAPIやフロントエンド側のStore設計はどうあるべきなのか、混乱してきたのが質問の背景になります!

※ EvansのDDD本にあるSmart UIのUIはViewとControllerの両方を含んだプレゼンテーション層を指していると解釈しています。

以下、メリデリ考えてみました。

画面単位のAPIエンドポイントを用意する場合

前提

画面単位のAPIエンドポイントとは、例えば、このteratailの個別質問詳細ページの場合だと、このページの表示に必要な情報(質問、回答一覧、関連質問一覧など)を1つのエンドポイントがまとめて返してくれるようなものをイメージして言っています。

メリット

  • フロント側は単に表示だけしていれば良く、バック側にビジネスロジックをすべて寄せることができる(あるとしても表示に関するフォーマットのロジックを持つくらい)

デメリット

  • 画面側の表示項目に変更が発生した場合など、API側の改修も必要になる
  • APIが画面に依存するかたちになる
  • 画面毎にAPIを叩く必要があるので、リソース単位のレスポンスと違って、既に取得済みのAPIレスポンスを使い回すことが出来ない
  • ビジネスロジックがバックとフロントの両方に散らばってしまうことになる(フロント側に関しては、フロント側の各画面でビジネスロジックが散らばらないように、store側にビジネスロジックを寄せることで、複数の画面に同じロジックが散らばることは避けられる)
  • 画面毎に対応する画面用のAPIエンドポイントを用意するので、似たような情報を含んだ情報を返すAPIエンドポイントが増えて気持ち悪い気がする
  • 画面毎に対応する画面用のAPIエンドポイントを用意するので、APIエンドポイントが増えがち
  • クライアントがブラウザー、アプリ、CLIと複数ある場合、クライアント毎に表示する情報が異なると思われるので、さらにAPIエンドポイントが増える

リソース単位のAPIエンドポイントを用意する場合

前提

リソース単位のAPIエンドポイントとは、例えば、このteratailの個別質問詳細ページの場合だと、このページに対応した質問情報を返すエンドポイントがあったり、回答一覧を返すエンドポイントがあったり、関連した質問情報を返すエンドポイントがそれぞれ存在しているようなものをイメージしています。

メリット

  • 画面側の表示項目に変更が発生した場合、フロントが既にバックから受け取っているデータを用いて、計算、加工することでAPIの改修が不要になることもある
  • 既に取得済みのAPIレスポンスをキャッシュして使い回すことができる場合がある(キャッシュして問題ないものに限る)

デメリット

  • フロント側で一部、ビジネスロジックを持つことが発生し得る。(独り言:リソース単位でフロントにレスポンス返すと、フロントでビジネスロジックを本当に持ち得えるのか?その処理はフロント側にあっても別に問題ないような処理だったりしないのか?つまり、ビジネスロジックであるのかどうか? 改めて、ビジネスロジックとは何かの定義と具体例を挙げて、検討した方が良さそう)

リソース単位のAPIエンドポイントと画面単位のAPIエンドポイントの両方を用意する???

メリデリ挙げてみて、両方のパターンの組み合わせを用意するのはどうなのだろうと思いつきました。
こういうのって、実際、やってたりするのでしょうか?

例えば、リソース単位のAPIを叩くのを基本としつつも、そのAPIでは取得出来ない画面固有の差分を取得するAPIエンドポイントを別途用意するみたいなイメージです。

よくわかっていないけど、BFF ???

BFFがあまりなんだかよくわかっていないですが、リソース単位のAPIエンドポイントは用意して、フロントエンド側の個々の画面に必要な情報をまとめたレスポンスを返すAPIエンドポイントをBFFに用意する?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

画面単位とリソース単位がかけ離れる状況ってなんですか?ひとつの画面に二つのリソースが必要ならAPI二つ叩くだけにした方がAPI利用する側も提供する側も仕様の共有し易いですよね?
という疑問はあるものの

基本の考え方はリソース単位ですね。

「要はバランスだよね」おじさん的

でしょうね。特定のクライアント向けという前提であれば。

メリデリ挙げてみて、両方のパターンの組み合わせを用意するのはどうなのだろうと思いつきました。

開発コストに問題がなければたくさん用意してあげたらいいでしょう。API利用するという事は相手も技術者なんだから、提供する側が方法を絞ってあげる必要はないですから。

加工までしてあげるんだったらそのまま表示箇所も作ってあげちゃった方が楽じゃないですか?客先向けのAPI仕様書を作る手間も省けて。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/10/02 08:46

    > 面単位とリソース単位がかけ離れる状況ってなんですか?

    かけ離れるという訳ではないのですが、フロント側でリソースAの情報の一部と、リソースBの情報の一部をそれぞれ組み合わせて、加工、判断、計算をしないといけないような場面が出てきてしまうと思っています。

    例えば、ログインユーザーのロールがXYZというロールで、かつ、帳票が生成済みの場合であれば、ダウンロードボタンを表示、または、活性化させたいような場合、ユーザーリソースに含まれるロール情報と、帳票情報に含まれるステータスの両方をみて、ダウンロード可能かどうかをフロント側で判断しないといけないみたいなケースです。

    キャンセル

  • 2020/10/02 12:28

    その例だと、DL可能かどうかでボタンの表示非表示を切り替える点、DLボタンのリンク先を知られれば条件満たしてなくてもアクセスされてしまう点、いずれもSPAじゃなくても同じ事ですよね?
    実際にDLを押された時の処理が有効か無効かはサーバー側で判定してレスポンス返すだけですよ。
    条件を満たしていなければエラーを返せばいいんです。

    示してくれた例だと↑の内容で済んでしまうので悩みどころが分かりません

    キャンセル

  • 2020/10/02 13:52

    なるほど、たしかに!実際はバック側で権限チェックしてDLできるかどうかは必ずみることになるので、上の自分のDLの話は、ビジネスロジックというよりかは、フロント側の表示に関するロジックになると捉えて、フロント側にロジックがあっても問題ないとする感じで良いですかね?

    キャンセル

  • 2020/10/02 21:25

    そうです。逆に聞きたいのですが、この回答とコメントを見た上で何が問題だと感じますか?それを聞いた方が返事し易い気が。

    APIサーバーは飽くまでも自身へのリクエストの正当性(APIへのアクセス権限やパラメーターなど、諸々のバリデーション)を検証し、問題無ければレスポンスを返すだけです。
    問題があれば単純に400エラーでもいいし、APIの仕様を推察されない程度にレスポンスにエラーの理由を含めてもいいです。

    キャンセル

+1

私の場合ですが画面単位とリソース単位の中間の設計にします。

もしくは、「要はバランスだよね」おじさん的な感じになるのでしょうか!? 

やはりこうなると思います。

理由としては
・APIのコマンド数はできるだけ抑えたい
→コマンド数増大は管理リスクも増大する

・ただ1度のAPIコマンドで大量データが取得されるのは困る。
→セキュリティの面でも一度に取得させたくない
→明細結果などはpageなどで一定量で区切りさせ処理する

・また1度のAPIコマンドですべて処理されるのも困る。
エラー発生時の対応が難しくなるのと、コマンドコピーされた場合のリスクが高くなる。

・拡張性は残したい
→あまりまとめすぎると影響が大きくなるので分散しておきたい。

処理単位としては

単票形式画面(更新あり)
認証用API(共通)
メイン読込用API(1レコードのみ)
選択リスト読込用API(コンボボックスなどのリスト用共通)
更新時データ送信用API(あくまでデータの送信のみ)
更新用データDB登録用API(ビジネスロジックを与えDBに登録する)

明細形式画面(更新なし)
認証用API(共通)
メイン読込用API(複数行だがページ単位とし一度に取得できる件数を絞る)

質問者様がおっしゃられているように一部のビジネスロジックは
JS側でカバーしないといけませんがメインはサーバ側。
ただし一回で登録させるのではなく2回に分ける。
(2回に分けた方がセキュリティ上も開発上も楽になる)

メイン読込用APIは画面の種類分用意
画面に必要ない余計な項目の値を通信したくないのと
共用にすると変更時の影響度が高くなる。
(共通化はサーバ内処理でとすればよい)

自分が設計したときはこの様なコンセプトでした。

あと個人情報の管理の観点から通信上は暗号化で行いJS側で復号しないといけないのが
意外と手間でしたね。

こうやって書いてみると機能単位の設計なるという事でしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/10/02 14:03

    > 質問者様がおっしゃられているように一部のビジネスロジックは
    JS側でカバーしないといけませんが

    すみません。自分が質問するのもおかしいのですが、フロント側で持ちそうな一部のビジネスロジックにはどのようなものがありそうでしょうか?自分がいまいち良い具体例を出せずちょっと困ってしまいまして。。。

    キャンセル

  • 2020/10/02 14:18

    そうですね
    例では
    組織図の選択式で
    支店「xxx支店」
    部・課「yyy部」
    担当者「Aさん」
    と選択できる項目を変化させる
    とかはフロント側で制御となります。

    また一部の入力規則もフロント側になるかと思います。
    A商品 単価100円 個数0個 → フロント側でエラー判定(必須入力判定)
    A商品 単価100円 個数10000個 → サーバ側で個数の妥当性を確認する。
    この時発注価格の合計計算および適正範囲かもサーバ側で行う。

    > フロント側で持ちそうな一部のビジネスロジック
    となっていますが私的には「入力規則レベル」と思っています。
    (それ以上をフロント側にもたすのはリスクがある。)

    キャンセル

  • 2020/10/02 14:55

    ありがとうございます!

    > 発注価格の合計計算および適正範囲かもサーバ側で行う

    合計値を画面上に表示する場合はフロント側で計算してひとまず合計値の表示はしてしまって良いでしょうか?ただし、発注のPostリクエストをなげるときはパラメーターに合計値を渡すのではなく、合計値を計算するために必要な情報を渡すみたいな感じになりますかね?

    それとも、合計値を画面上に表示する場合は、PostメソッドのAPIエンドポイントにリクエストを送る前に、バリデーションを行うリクエストを投げて、レスポンスで合計金額を返してもらって画面上に合計値を表示することになりますでしょうか?

    前者のメリットはすぐに合計値を表示できるので、UX的な面で良いが、デメリットはフロントにも合計計算ロジックが散らばる。後者のメリットはその逆、といったかたちで、この辺はUXを重視するならば、前者を選ぶみたいなトレードオフとなりますか?

    キャンセル

  • 2020/10/02 15:09 編集

    画面表示自体はフロントでよいかと思います。
    1. 合計の表示はフロント側でリアルタイムに更新
    2. 「保存」ボタン等でAPIにコマンド送信しDBへの追加更新処理を働かします。
    3. 再度サーバ側でも合計値を計算、これがエラーになった場合APIの応答で合計値エラーを返します。
    4. API保存処理でエラーになったので画面上「合計値エラーです」と表示させます。(メッセージや赤枠表示等)
    APIでデータを流す際はできるだけ余計な値は入れないほうが良いです。
    (合計などはAPIサーバ側とフロント側2箇所に分けて)

    1つのほうが便利そうなのですが開発担当をAPIサーバ側とフロント側に分けた際
    APIサーバ側が完成しないとフロント側が作業できなくなるので効率が悪いんです。
    あとテスト自体もAPIサーバ側とフロント側別々で行えます。
    セキュリティ上も余計な情報はできるだけ通信に載せたくないのも理由の一つです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る