web-dev-qa-db-ja.com

API設計:具体的なアプローチと抽象的なアプローチ-ベストプラクティス?

システム間(ビジネスレベルで)のAPIについて議論する場合、チームには2つの異なる視点があることがよくあります。 ジェネリック 抽象的アプローチ、その他の簡単な「コンクリート」アプローチ。

例:単純な「個人検索」APIの設計。具体的なバージョンは

 searchPerson(String name, boolean soundEx,
              String firstName, boolean soundEx,
              String dateOfBirth)

具体的なバージョンを支持する人々は言う:

  • aPIは自己文書化されています
  • わかりやすい
  • 検証が簡単です(コンパイラーまたはWebサービスとして:スキーマ検証)
  • KISS

私たちのチームの他のグループは、「それは単なる検索基準のリストです」と言います。

searchPerson(List<SearchCriteria> criteria)

SearchCritera {
  String parameter,
  String value,
  Map<String, String> options
}

いくつかの列挙型の「パラメータ」を作成する可能性があります。

支持者は言う:

  • aPI(の宣言)を変更せずに、実装を変更できます。基準またはオプションを追加します。展開時にそのような変更を同期しなくても。
  • 具体的なバリアントでもドキュメントが必要です
  • スキーマ検証は過大評価されており、多くの場合、さらに検証する必要があります。スキーマはすべてのケースを処理できるわけではありません
  • 私たちはすでに別のシステムで同様のAPIを持っています-再利用

反論は

  • 有効なパラメータと有効なパラメータの組み合わせに関する多くのドキュメント
  • 他のチームにとって理解が難しいため、コミュニケーションの労力が増える

ベストプラクティスはありますか?文献?

25
erik

それはあなたが話しているフィールドの数とそれらがどのように使用されるかに依存します。フィールド数が少ない高度に構造化されたクエリには具象が適していますが、クエリが非常に自由形式である傾向がある場合、具象アプローチは3つまたは4つを超えるフィールドではすぐに扱いにくくなります。

一方、汎用APIを純粋に保つことは非常に困難です。多くの場所で単純な名前検索を実行すると、最終的に誰かが同じ5行のコードを繰り返すのに飽きて、関数にラップすることになります。このようなAPIは、常に最も一般的に使用されるクエリの具体的なラッパーと共に、汎用クエリのハイブリッドに進化します。そして、私はそれで何も悪いとは思わない。それはあなたに両方の長所を与えます。

18
Karl Bielefeldt

優れたAPIの設計は芸術です。時間が経っても良いAPIは高く評価されます。私の意見では、抽象的なコンクリートの線に一般的なバイアスはないはずです。いくつかのパラメーターは曜日と同じくらい具体的ですが、いくつかは拡張性のために設計する必要があります(そして、関数名の一部など、それらを具体化するのはかなり愚かです)、さらに別のパラメーターはさらに洗練されたものにすることができますコールバックを提供するために必要なAPIまたはドメイン固有の言語でさえ、複雑さとの戦いに役立ちます。

月の下ではめったに新しいことが起こりません。従来の技術、特に確立された標準と形式を確認してください(たとえば、フィードの後に​​多くのものがモデル化され、イベントの説明はical/vcalで作成されました)。 APIを簡単に追加できます。頻繁に存在するエンティティが具体的で、想定される拡張機能が辞書です。特定の状況に対処するための確立されたパターンもいくつかあります。たとえば、HTTPリクエスト(および類似のもの)の処理は、RequestオブジェクトとResponseオブジェクトを使用してAPIでモデル化できます。

APIを設計する前に、含まれないものも含め、さまざまな側面についてブレインストーミングを行いますが、注意する必要があります。そのような例は、言語、記述方向、エンコーディング、ロケール、タイムゾーン情報などです。複数が出現する可能性がある場所に注意してください。単一の値ではなく、リストを使用してください。たとえば、ビデオチャットシステムのAPIを設計している場合、2人ではなくN人の参加者を想定すると、APIはより便利になります(現時点での仕様はそうですが)。

時々、抽象であることは複雑さを大幅に減らすのに役立ちます:3 + 4、2 + 2、および7 + 6のみを追加する計算機を設計した場合でも、X + Yを実装する方がはるかに簡単かもしれません(XおよびY、およびADD_3_4()、ADD_2_2()などの代わりにAPIにADD(X、Y)を含めます...

全体として、いずれかの方法を選択することは、技術的な詳細にすぎません。文書では、頻繁な使用例を具体的な方法で説明する必要があります。

データ構造側で行うことは何でも、APIバージョンのフィールドを提供します。

要約すると、APIはソフトウェアを処理するときに複雑さを最小限に抑える必要があります。 APIを正しく評価するには、公開されている複雑さのレベルが適切である必要があります。 APIの形式の決定は、問題のあるドメインの安定性に大きく依存します。したがって、この情報は複雑さの方程式に影響を与える可能性があるため、ソフトウェアとそのAPIがどの方向に成長するかについて、ある程度の見積もりが必要です。また、API設計は人々が理解できるように用意されています。現在のソフトウェアテクノロジー分野に良い伝統がある場合、それを理解するのに役立つので、それらからあまり逸脱しないようにしてください。誰のために書くかを考慮に入れてください。より上級のユーザーは一般性と柔軟性を高く評価しますが、経験が少ないユーザーはコンクリートに慣れているでしょう。ただし、そこにいるAPIユーザーの大多数、つまり初心者と専門家の間のユーザーに注意してください。

文献の面では、美しさは隠された最適性(およびある目的への適合性)を知覚することだと思うので、「美しいコード」の主要プログラマーがアンディオラム(グレッグウィルソン)の考え方を説明することをお勧めします。

7
Roman Susi

抽象APIの検証は必ずしも難しいとは言えません。条件パラメーターが十分に単純で、相互の依存関係がほとんどない場合、パラメーターを個別に渡すか配列で渡すかには大きな違いはありません。すべてを検証する必要があります。しかし、それは基準パラメーターとオブジェクト自体の設計に依存します。

APIが十分に複雑である場合、具体的なメソッドを持つことはオプションではありません。ある時点で、パラメータが多すぎるメソッドか、必要なすべてのユースケースをカバーできない単純なメソッドが多すぎる可能性があります。消費APIの設計における私の個人的な経験としては、APIレベルでより一般的なメソッドを用意し、アプリケーションレベルで特定の必要なラッパーを実装することをお勧めします。

1
Pavels

変更の議論はYAGNIで却下されるべきです。基本的に、実際にジェネリックAPIを異なる方法で使用する3つの異なるユースケースがない限り、次のユースケースが発生したとき(およびユースケースがあるとき)に変更する必要がないように設計する可能性はかなり低いです。場合、明らかに一般的なインターフェース、ピリオドが必要です)。したがって、変更を試みて準備をしないでください。

どちらの場合も、デプロイメントのために変更を同期する必要はありません。後でインターフェースを一般化するときに、下位互換性のために、より具体的なインターフェースをいつでも提供できます。しかし実際には、どのデプロイメントにも非常に多くの変更があるため、とにかくそれを同期するので、中間状態をテストする必要はありません。私もそれを議論とは思わないでしょう。

ドキュメンテーションに関しては、どちらのソリューションも使いやすく、明白かもしれません。しかし、それは重要な議論として立っています。実際のケースで使いやすいようにインターフェースを実装します。場合によっては、特定の方が良い場合もあれば、ジェネリックの場合もあります。

1
Jan Hudec

私は抽象インターフェースのアプローチを支持します。これらの種類の(検索)サービスにクエリを送信することは一般的な問題であり、おそらく再び発生します。さらに、より一般的なインターフェースを再利用するのに適した、より多くのサービス候補が見つかるでしょう。これらのサービスに一貫性のある共通インターフェースを提供できるようにするために、インターフェース定義で現在識別されているクエリパラメータを列挙しません。

以前指摘したように、インターフェイスを変更せずに実装を変更または拡張する機会が好きです。別の検索条件を追加しても、サービス定義に反映される必要はありません。

明確で簡潔なインターフェースを設計することは間違いありませんが、常に追加のドキュメントを提供する必要があります。有効な検索条件の定義スコープを追加しても、それほど負担はありません。

1
0x0me

私が今まで見た中で最高の要約は、Rustyのスケールで、現在は RustyのAPI設計マニフェスト と呼ばれています。私はその1つだけを強く推奨できます。完全を期すために、最初のリンクからのスケールの要約を引用します(上にあるほど、下にあるほど悪い)。

良いAPI

  • 間違えることは不可能です。
  • コンパイラー/リンカーはそれを誤解させません。
  • 間違った場合、コンパイラーは警告を出します。
  • 明らかな使用法は(おそらく)正しいものです。
  • 名前から使い方がわかります。
  • 正しく実行しないと、実行時に常に壊れます。
  • 一般的な慣習に従ってください。そうすれば、正しく理解できます。
  • ドキュメントを読むと、正しく理解できます。
  • 実装を読んで、正しく理解してください。
  • 正しいメーリングリストのスレッドを読んで、正しく理解してください。

Bad API

  • メーリングリストのスレッドを読んでください。
  • 実装を読んでください。間違っているでしょう。
  • ドキュメンテーションを読んでください、あなたはそれを誤解するでしょう。
  • 一般的な慣習に従ってください、そうすれば間違ってしまうでしょう。
  • 正しく実行すると、実行時に壊れることがあります。
  • 名前はそれを使用しない方法を教えてくれます。
  • 明らかな使い方は間違っています。
  • 正しい場合、コンパイラーは警告を出します。
  • コンパイラー/リンカーはそれを正しくさせません。
  • 正しくすることは不可能です。

詳細ページ herehere には、各ポイントの詳細な説明が付いています。これは、APIデザイナーにとって必読の記事です。これを読んだことがあれば、Rustyに感謝します。

1
JensG

私の個人的な好みは抽象的なものですが、私の会社の方針は私を具体的なものに固定します。それが私にとっての議論の終わりです:)

あなたは両方のアプローチについて長所と短所をリストアップする良い仕事をしました、そしてあなたが掘り続ければあなたは双方に賛成するたくさんの議論を見つけるでしょう。 APIのアーキテクチャが適切に開発されている限り、つまり、今日のAPIの使用方法と、将来的にどのように進化および成長するかについて考えた上で、どちらの方法でも問題ありません。

ここに私が反対の視点で持っていた2つのブックマークがあります:

好きな抽象クラス

お気に入りのインターフェース

「APIは私のビジネス要件を満たしていますか?成功のための明確な基準はありますか?拡張できますか?」と自問してください。これらは従うべき本当にシンプルなベストプラクティスのように見えますが、正直なところ、具体的なものよりも一般的なものよりもはるかに重要です。

1
gws2

素人の言葉で:

  • 抽象アプローチには、その周りに具象メソッドを構築できるという利点があります。
  • 逆は正しくありません
0

SearchCriteriaのアイデアを少し拡張すると、ANDORなどの基準を作成するなどの柔軟性が得られます。このような機能が必要な場合は、これがより良いアプローチです。

それ以外の場合は、使いやすさを考慮して設計してください。それを使用する人々のためにAPIを簡単にします。頻繁に必要になる基本機能(名前で人を検索するなど)がある場合は、それらを直接提供します。上級ユーザーが高度な検索を必要とする場合でも、SearchCriteriaを使用できます。

0
Uooo

APIの背後にあるコードは何をしていますか?柔軟なものであれば、柔軟なAPIが適しています。 APIの背後にあるコードが非常に具体的である場合、その上に柔軟な顔をすることは、APIのユーザーがイライラし、APIが偽っているすべてのことでイライラすることになり、実際には達成できないことを意味します。

個人検索の例では、3つのフィールドすべてが必要ですか?その場合、単純に機能しない多数の使用が可能になるため、基準リストは不適切です。そうでない場合、ユーザーに不要な入力を指定するように要求することは悪いことです。 V2でアドレスによる検索が追加される可能性はどのくらいありますか?柔軟なインターフェースにより、柔軟性のないインターフェースよりも簡単に追加できます。

すべてのシステムが非常に柔軟である必要はありません。すべてを作成しようとすると、建築宇宙飛行もそうです。柔軟な弓が矢を放ちます。柔軟な剣はゴム鶏と同じくらい便利です。

0
stonemetal