関数ベースのRESTful APIの設計


8

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

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

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

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

私のアプローチ:

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

サードパーティは、製品のクエリ、現在のStockQuantity購入数量に基づく計算、およびPUT新しい値でのリクエストの送信を担当します。

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

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

したがって、計算を行って、 StockQuantity

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

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


PUTは(理論的には)べき等であるべきであることに注意してください。オプション1はセマンティクスに適合します。オプション2はしません。RESTに準拠することが重要です。多くの頭痛の種からあなたを救うので、PUT呼び出しをべき等に保つようにしようとします。DELETEについても同様です。コマンドのような操作の場合、正直に言って、JsonまたはXML RPCにチャンスを与えます。両方の戦略(RESTおよびRPC)は、同じWeb API内で共存できます。CQRSの原則(コマンドクエリの責任の分離:-)で伝えます
Laiv

回答:


19

サードパーティに製品への売り上げを通知させることができます。例えば:

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

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

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


2
この。プラス:各販売にもデータベース上のオブジェクトが必要なようです。各セールをdbの個別のオブジェクトとして持つことで、トレーサビリティが可能になります。何かがうまくいかず、最終的な在庫数量が間違っていて、値を修正する必要がある場合を考えてください。最終値の列が1つしかない場合、できることは多くありません。うまくいけば、何がうまくいかなかったかを理解するために、システムに役立つログがあります。タイムスタンプ、ユーザー名、場合によってはIPアドレスが付加された販売オブジェクトがある場合、特定のレコードを削除してデータを修正し、それがどのユーザー/場所から来たかを追跡することができます。
スキー

入力ありがとうございます。販売/注文は別のチームのリソースであり、それらを保存または処理することは私の責任ではありません。これを知って、/saleエンドポイントの作成はまだ有効ですか?
SefaÜmitOray 18年

@SefaÜmitOray:エンドポイント/sale/product/{id}/saleは完全に独立しており、それらが類似した名前を持っているという事実は、それらが同じリソースを参照していることを決して意味しません。
Bart van Ingen Schenau

@BartvanIngenSchenau私が言っていることは、sale私のドメインにはなく、の一部ではありませんproduct/product/{id}/sale実際のリソースを表していないときに作成しても意味がありますか?
SefaÜmitOray 18年

5
@SefaÜmitOrayそれがあなたの文脈で意味のある何かを表す場合、それは完全に有効です。他のコンテキストと同じ意味である必要はなく、データベースに直接永続化されるものである必要もありません。ドメイン!=データベーステーブル、リソース!=データベーステーブル。
RobertBräutigam18年

3

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

POSのコンテキストでは、個々のトランザクションを追跡することは非常に理にかなっています。そこでは、ロバートのソリューションは非常に理にかなっています。

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

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

理にかなっています。

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

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

だから、あなたが提供した文脈に基づいて、セマンティクスが明確であるとは思いません。

HTTP部分については、PUT [target-uri]ドキュメントのある表現を別の表現に置き換える場合、意味的に意味があります。それはだUPSERT-リソースへの第二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

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に変換するためのコードオンデマンド拡張が必要です。


1
エンドポイントがあるからといって、個々の売上を追跡する必要はありません。つまり、POSTできるからといって、以前のセールスコールをリストできるようにする必要はありません。ただし、他のユースケース(わからない)が存在する可能性があるため、条件付き呼び出しまたはその他の手段でべき等呼び出しを定義する必要があります。
RobertBräutigam18年

2

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

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

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

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

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


1

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

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

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

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


1

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

在庫カウントなどの内部使用の場合は、アプローチを使用できますPUT /api/Product/{Id}/ --data { "StockQuantity": "{NewStockQuantity}" }

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.