web-dev-qa-db-ja.com

関数ベースのRESTful APIの設計

私と友人の間で議論を解決してください。

現在、製品APIを設計しています。製品エンティティは次のようになります

{
    "Id": "",
    "ProductName": "",
    "StockQuantity": 0
}

製品の販売はサードパーティが処理し、StockQuantityフィールドを減らすことができるように、購入数量を通知する義務があります。

私のアプローチ:

PUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }

サードパーティは、製品のクエリを実行し、現在のStockQuantityと購入数量に基づいて計算を行い、新しい値でPUTリクエストを送信する責任があります。

私の友人はサードパーティに計算を望まない。彼のアプローチ

PUT /api/Product/{Id}/DecreaseStock --data { "PurchasedQuantity": "{PurchasedQuantity}" }

したがって、計算を行ってStockQuantityを更新できます

私は関数ベースのエンドポイントを作成したくありません、そして彼は計算を行うためにサードパーティを信頼したくありません。

この問題に取り組むための正しい方法は何でしょうか?

8
Sefa Ümit Oray

あなたはあなたのサードパーティにあなたの製品にsalesを投稿させることができます。例えば:

POST /product/{id}/sale { "Quantity": 3 }

私はあなたとあなたの同僚の両方の意見に同意します。これはビジネスロジックであり、APIのクライアントに任せるべきではありませんが、エンドポイントとして「関数」を持つことも避けてください。

時々そのような問題を解決することは、それを別の呼び方をするのと同じくらい簡単です。

19

あなたもできない理由はありません。または両方。

POSのコンテキストでは、個々のトランザクションを追跡することは非常に理にかなっています。そこで、 Robert's solution は非常に理にかなっています。

在庫/倉庫のコンテキストでは、「在庫を取る」ほどトランザクションを追跡する必要はありません。クライアントが在庫レベルを報告できるエンドポイントを持っている

10ユニット、7ユニット、3ユニット、20ユニット

理にかなっています。

在庫レベルは「販売」以外の理由で変化します。心に留めておくべきことだけです。

理論的には、在庫レベルは変更から計算可能でなければなりません。しかし、一部のドメインでは、検証する前提とまったく同じです。在庫レベルを2つの異なる方法で計算し、不一致(別名「収縮」)をチェックできるようにする必要があります。

したがって、あなたが提供したコンテキストに基づいて、セマンティクスが明確であるとは思いません。

HTTP部分については、 PUT [target-uri]は、ドキュメントの1つの表現を別の表現に置き換える場合に意味的に意味があります。これはUPSERTです。リソースへの2番目のPUTは、既存の表現の上書きを要求しています。

PUT /sales { Quantity = 5 }
PUT /sales { Quantity = 2 }
PUT /sales { Quantity = 3 }

販売数量は3ではなく10であると述べています。

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }

10は次のようになります

PUT /sales { Quantity : [5] }
PUT /sales { Quantity : [5,2] }
PUT /sales { Quantity : [5,2,3] }

これが10のもう1つのスペルです。

POST /sales { Quantity = 5 }
POST /sales { Quantity = 2 }
POST /sales { Quantity = 3 }

HTTPに関する限り、これも許容されます。ただし、メッセージが重複する場合があるため、 信頼性の低いネットワーク には適していません。

POST /sales { Quantity = 5 }
POST /sales { Quantity = 2 }
POST /sales { Quantity = 3 }
POST /sales { Quantity = 3 }

それは13ですか?または10

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }
PUT /sales/3 { Quantity = 3 }

それは明白です10

PUT /sales { Quantity : [5,2,3] }
PUT /sales { Quantity : [5,2,3] }

それは明白です10

PUT /sales/1 { Quantity = 5 }
PUT /sales/2 { Quantity = 2 }
PUT /sales/3 { Quantity = 3 }
PUT /sales/4 { Quantity = 3 }

それは明白です13

PUT /sales { Quantity : [5,2,3] }
PUT /sales { Quantity : [5,2,3,3] }

それは明白です13

POST /sales { TransactionId = 1 , Quantity = 5 }
POST /sales { TransactionId = 2 , Quantity = 2 }
POST /sales { TransactionId = 3 , Quantity = 3 }
POST /sales { TransactionId = 3 , Quantity = 3 }

10

POST /sales { TransactionId = 1 , Quantity = 5 }
POST /sales { TransactionId = 2 , Quantity = 2 }
POST /sales { TransactionId = 3 , Quantity = 3 }
POST /sales { TransactionId = 4 , Quantity = 3 }

13

(公平を期すために、HTTPは 条件付き要求 をサポートしています。ドメイン固有のプロトコルからドメインに依存しないヘッダーにメタデータの一部を持ち上げて、あいまいさの一部を排除することができます。一緒に遊ぶクライアント)。

もちろん、トレードオフがあります。HTMLはネイティブのPUTサポートを備えていません。 APIのクライアントをブラウザーにする場合は、POSTに基づくプロトコルが必要か、フォーム送信をPOSTから変換するためのコードオンデマンド拡張が必要です_ PUTに。

3
VoiceOfUnreason

これはどのようにスライスしても、本当に悪いデザインのようです。私が倉庫を管理するために彼らを雇わなければ、私が現在の在庫を教えてくれることを第三者に信用することは決してありません。

さらに、関数のようなアプローチはRESTfulではなく、コンシューマー間で驚きを生み出すことになります。

最後に、あなたがセールについて気にかけている唯一の事柄が、それが終わった後にあなたが残した結果として生じる在庫であるというシナリオを想像することはできません。

サードパーティにセールまたは請求書のリソースを投稿してもらう方がはるかに便利です(製品、数量、日付、配送方法、顧客情報などの情報が含まれます)。これにより、実際に販売している商品、販売先、販売先などを実際に分析および追跡できるので、ビジネスを実際に整理できます。

サードパーティが完全な注文処理を行っている場合でも、会計と顧客の人口統計の目的で販売を追跡する必要があります。

2
Paul

PUT/api/Product/{Id}/--data {"StockQuantity": "{NewStockQuantity}"}

この種の設計には大きな問題があり、APIに対して複数のクライアントスレッドを実行したい場合、ダーティな読み取り/書き込みが発生する可能性があります。つまり、クライアントが現在の数量を引き下げてから新しい値を計算するまでの間に、別のクライアントが同じ以前の値を引き、別の答えを計算できます。最終的に得られる数量は、1つの更新が最後になるかどちらかになりますが、どちらも正しくありません。たとえば、現在の数量が10であるとします。クライアント[〜#〜] a [〜#〜]は5つのアイテムを販売したいと考えており、現在の数量を引き出します。同時に、クライアント[〜#〜] b [〜#〜]は6つのアイテムを販売し、現在の数量を引き出します。どちらの商品も10個の在庫があります。 [〜#〜] a [〜#〜]は、残り5アイテムを計算します。 [〜#〜] b [〜#〜]は残り4を計算します。どちらも更新されます。更新が最後に記録された人に応じて、残りの4つまたは5つのアイテムが表示されます。ただし、実際に持っている商品の数が増えました。さらに悪いことは、簡単にウォークスルーして問題の原因を確認する方法がないことです。ログにある2つのPUTsが正しくありません。

現実の記録システムでは、単に現在の合計を取得するだけでは不十分です。店に行って多数の商品を購入する場合を検討してください。あなたは領収書を求め、レジ係はあなたに単一の合計で伝票を手渡します。その領収書の合計が正しいことをどのように示しますか?何かを返品したい場合、商品を購入したことをどのように示しますか?

あなたの友人のアプローチはより良いですが、ミックスにトランザクションIDを追加することをお勧めします。これは、VoiceOfUnreasonが重複したトランザクションについて言及する本当の懸念に対処します。 1つのオプションは、新しいトランザクションを作成するPOST操作を提供し、次にそのトランザクションにPUTを提供して確認することです。確認の時点で、十分な在庫がないため、総在庫を減らすか、要求を拒否します。

1
JimmyJames

販売はサードパーティによって処理されるため、在庫数を更新できないようにして、製品在庫を管理する必要があります。

内部使用のため。在庫カウントの目的で、あなたはあなたのアプローチを持つことができます、すなわちPUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }

外部で使用する場合は、別のインターフェースを作成する必要があります。 /api/SalesOrder/は、次のような製品と数量のリストを取ります。

POST /api/SalesOrder/ --data { [{"Id": 1, "Qty": 1}, {"Id": 2, "Qty": 3}] }

サードパーティから送信されたSalesOrderに基づいて、各製品の数量を更新して注文に割り当てることができます。十分な製品がない場合は注文を拒否できます。

処理と在庫カウントは内部プロセスであり、サードパーティはインターフェースを必要とするだけなので、注文を在庫に転送できます。基本的に、SalesOrderは、セールス、ファイナンス、およびウェアハウスが通信して販売を完了する方法です。

1
imel96