私の会社は、いくつかの内部アプリケーションで使用されるRESTful APIを開発しました。このAPIを使用すると、クライアントは1回限りの支払いと定期的な支払いを請求できます。これらは両方ともリソースで構成されています。 a personsubscriptionを作成するにはpayment methodが必要ですtransactionを作成します。データは非常にリレーショナルですが、ワークフローの1つは次のとおりです。
新しい人が定期的な支払いを設定しようとしていると仮定します。ワークフローは次のようになります。
これらの各操作は、アプリケーションのREST APIを構成するコントローラーの1つのエンドポイントによって表されます。
アプリが成長するにつれ、多くの人々がなぜに疑問を投げかけ、このモジュラーAPI設計にこだわり続けています。新しいクライアントがサブスクリプションを作成するためにこのアプリケーションと統合する必要があるときはいつでも、それらの4つのステップに従うコードを実装する必要があります。私たちの答えは、「優れたソフトウェア設計とRESTにより、懸念の分離を促進し、あるタイプのリソースの構築を別のタイプのリソースから分離しておくことで、物事を柔軟で保守しやすい状態に保つことができます」です。これに対する主な対抗点は、他のすべてのソフトウェアと同様に、このプロセスのステップの間にいくつかのバグが発生したことであり、1つのエンドポイントですべてを実行すれば問題は解決される(または問題が少なくなる)と人々は主張しています。
このプロジェクトは数年前のものであり、多くの本番環境での使用が見られます。 「すべてを行う」という1つのエンドポイントが正しいことだと信じるのは非常に躊躇していますが、この議論で私がしなければならないのは理論と原則だけだと感じています。私たちの問題のいくつかはシステム間のびびりを減らすことでおそらく解決されたでしょうが、いくつかの異なる操作を1つのメソッドに入れてそれを1日で呼び出すことは本質的に間違っているようです。
何か足りないものはありますか?この時点までにアプリの設計を手伝ったからといって偏ったのでしょうか、それとも他の開発者が今日の問題を解決するためにメンテナンスと設計を犠牲にしているのでしょうか?
私は理論/意見ではなく、実際の経験に基づいたいくつかの答えを探しているので、この質問が「主に意見に基づく」ものとして閉じられないことを願っています(すでに理論的には正しいと信じているため) 。
<目標がどのように価値を提供し、RESTfulであるか、またはいくつかのOOADの方針に従うかについての怒りを挿入することは、それ自体、YAGNIにも終わりではありません/>
RESTについて(よくある)誤解があると思います。これが当てはまるかどうかはわかりませんが、あなたが言ったことのいくつかは、RESTリソースとデータベーステーブルの間でほぼ1対1の対応がある可能性があることを示唆しています。あなたがそうしないと、あなたが表現する「ジレンマ」は誤ったものです。RESTやその他の設計原理についての何も、/person/3/recurring_payment/
1同じエンティティを参照する複数のリソースを持つことは完全に問題なく、期待されます 。 POSTこのようなリソースに対しては、支払い方法、サブスクリプション、およびトランザクションを作成するすべての作業を行う可能性があります。これにより、/person/3/recurring_payment/1
リソース。これは、作成されたトランザクションの単なるエイリアスである場合や、すべての関連データの要約を提供する場合があります。
明らかに、ソフトウェア設計の原則には、機能を組み合わせて一般的な繰り返しパターンをキャプチャする便利な機能を提供できないことは明記されていません。あなたはまだ一貫した概念的な実体を見つけようとするべきです。実際、現在のAPIが実装の詳細を本質的に公開していないことは私にはわかりません。
最後に、個人的には、RESTが多くの場合内部APIに実際に適しているとは思いません。RESTの目的は、長いAPIを作成することです存続期間があり、個別の管理ドメイン(個別の会社など)によって独立して進化可能であり、ゆっくりと変化する粗いドキュメントに対応できるように拡張可能2。これらのプロパティは望ましいですが、多くの場合、内部APIの要件ではありません。さらに、それらを達成すると、実装と仕様の複雑さ、および遅延の増加などの問題が追加されます。その結果、実際には多くのAPIは名前のみ RESTです 。これはあなたのAPIに当てはまる可能性が高いです。たとえば、anyメディアタイプを指定しましたか?そうでない場合は、おそらくRESTを実行していません。どちらでもかまいませんが、漠然と努力するのをやめるべきでしょう。また、RESTとは明らかに異なる方向に目を向けている新しいAPIパターンもあります。 GraphQL 。時間がどのようになるかはわかりますが、RESTはAPI設計の完全な理想ではありません。
1 この提案は、具体的な話をするためのものです。 RESTについては、よくある誤解があります。これは、URIパスを特定の方法で整理することに関するものです。実際には、RESTはURIを気にしませんパスは体系化されており、理想的にはクライアントは完全にそれを知らないことができます(そうすべきです)。これは [〜#〜] hateoas [〜#〜] のようなものにつながります。
何か足りないものはありますか?
はい。そもそも、APIがあまり良くないということはありません。
例を詳しく見てみましょう
新しい人が定期的な支払いを設定しようとしていると仮定します。ワークフローは次のようになります。
- 人を作成する
- その人にリンクされた支払い方法を作成する
- その支払い方法にリンクされたサブスクリプションを作成します
- そのサブスクリプションにリンクされているトランザクションを作成する
翻訳済み:APIコンシューマーはoneタスクを達成しようとしています。そのためには、4つの異なるサブタスクのオーケストレーションが必要です。そして、あなたのAPIは、消費者がオーケストレーションを達成する作業を実行することを要求しています。
消費者が求めているのは、1つのメッセージをコンシェルジュに送信することです。そして、コンシェルジュがそのメッセージをコンポーネントに分解し、ディスパッチを管理します。
Googleホームページは、これを完璧に説明するものでした。そのページからGoogleの検索プロトコルへのリンクがありました。リソースは、検索に一致した結果のカタログをナビゲートするためのリンクを提供します。しかし、Googleの「私は幸運を感じています」プロトコルへの秒リンクもありました。これは、検索プロトコルの細かい制御を、広告なしの便利なエクスペリエンスと交換しました。
APIリソースが複数のプロトコルをサポートしてはならないという理由は絶対にありません。これは、同じドメインの概念を表す多くのリソースがあり、各リソースが特定のプロトコルに特化していることを意味する場合があります。それはRESTのnormalです。
ジムウェバー、講演 REST:DDD In the Large
URIはドメインオブジェクトにマップされません-これはカプセル化に違反しています。作業(例:ドメインモデルへのコマンドの発行)は、リソース管理の副作用です。つまり、リソースは腐敗防止層の一部です。統合ドメインには、ビジネスドメインでビジネスオブジェクトを実行するよりもはるかに多くのリソースが必要です。
つまり、RESTは、顧客のAPIエクスペリエンスの向上を妨げるような障害を課しません。
代わりにAPIが提供するものはVogon官僚機構に似ています。1つの定期的な支払いで、4つの異なるフォームに記入し、4つの異なる部門のそれぞれに手渡しする必要があります。
APIコンシューマは、プロトコルが4つの異なるマイクロサービスに存在する4つの異なるデータベースを持つ4つの異なるドメインオブジェクトとして実装されていることを気にする必要はありません。APIのポイントは、コンシューマを詳細から隔離することです将来、詳細をより簡単に変更できるようにするためです。