web-dev-qa-db-ja.com

RESTfulな設計:サブリソースを使用する場合

リソース階層を設計するとき、いつサブリソースを使用する必要がありますか?

私は、リソースが他のリソースなしでは存在できない場合、そのサブリソースとして表されるべきだと信じていました。私は最近この反例に出くわしました:

  • 従業員は、すべての企業で一意に識別できます。
  • 従業員のアクセス制御とライフサイクルは会社に依存します。

私はこれを次のようにモデル化しました:/companies/{companyName}/employee/{employeeId}

従業員を見つけるために会社を調べる必要はないので、注意してください。もしそうなら、私は必要のない情報を調べるために代金を払っています。そうしないと、このURLは誤ってHTTP 200を返します。

/companies/{nonExistingName}/employee/{existingId}

  1. belongsへのリソースが別のリソースであるという事実をどのように表現すべきですか?
  2. リソース識別できません別のリソースがないという事実をどのように表す必要がありますか?
  3. サブリソースとはどのような関係を意味し、モデル化することを意味しませんか?
52
Gili

1年後、私は次の妥協で終了しました(一意の識別子を含むデータベース行の場合):

  1. すべてのリソースにルートで正規URIを割り当てます(例、_/companies/{id}_および_/employees/{id}_)。
  2. リソースが別のリソースなしでは存在できない場合、そのリソースはサブリソースとして表される必要があります。ただし、操作を検索エンジンクエリとして扱います。つまり、すぐに操作を実行する代わりに、正規のURIを指すHTTP 307 ("Temporary redirect")を返すだけです。これにより、クライアントは正規のURIに対して操作を繰り返します。
  3. 仕様ドキュメントは、概念モデルに一致するルートリソースのみを公開する必要があります(実装の詳細に依存しません)。実装の詳細は変更される可能性があります(行が一意に識別できなくなる可能性があります)が、概念モデルはそのまま残ります。上の例では、_/companies_ではなく_/employees_についてクライアントに伝えます。

このアプローチには次の利点があります。

  1. 不要なデータベース検索を行う必要がなくなります。
  2. 健全性チェックの数をリクエストごとに1つに減らします。せいぜい、従業員が会社に属しているかどうかを確認する必要がありますが、_/companies/{companyId}/employees/{employeeId}/computers/{computerId}_に対して2つの検証チェックを行う必要はなくなりました。
  3. データベースのスケーラビリティにさまざまな影響があります。一方では、より短い時間でより少ないテーブルをロックすることにより、ロックの競合を減らします。しかし一方で、各ルートリソースは異なるロック順序を使用する必要があるため、デッドロックの可能性が増加しています。これが正味の利得なのか損失なのかはわかりませんが、とにかく データベースのデッドロックを防ぐことはできません であり、結果のロックルールの理解と実装は簡単です。疑わしい場合は、シンプルさを選択してください。
  4. 概念モデルはそのままです。仕様ドキュメントが概念モデルのみを公開するようにすることで、既存のクライアントを壊すことなく、将来的に実装の詳細を含むURIを自由に削除できます。仕様で構造が未定義として宣言されている限り、中間URIで実装の詳細を公開することを妨げるものはありません。
15
Gili

ユーザーが特定の会社に属していることはもはや明らかではないため、これには問題があります。

これにより、ドメインモデルの問題が明らかになる場合があります。ユーザーが会社に属しているのはなぜですか?会社を変えた場合、まったく新しい人ですか? 2つの会社で働いている場合はどうなりますか?二人は違うの?

答えが「はい」の場合、ユーザーにアクセスするために会社固有の識別子を使用してみませんか?

例えばユーザー名:

company/foo/user/bar

barは、特定の会社の名前空間内で一意のユーザー名です)

答えが「いいえ」の場合、なぜ私はユーザー(人)ではないのか、そしてcompany/usersコレクションは単に私を指しているだけです:<link rel="user" uri="/user/1" />(注:従業員がより適切であると思われる)

あなたの特定の例の外では、リソースとリソースの関係は、所有権ではなくseになるとより適切であると思います(そしてそれが冗長性に苦しんでいる理由です暗黙的に会社を識別するユーザーの会社を識別する方法)。

これが意味するのは、usersは実際には会社のリソースのサブリソースであるということです。なぜなら、seは会社とその従業員の関係を定義するためです-別の言い方ですつまり、従業員の雇用を開始する前に会社を定義する必要があります。同様に、ユーザー(人)を募集する前に定義(生まれ)する必要があります。

17
Doug Moscrop

サブリソースとしてリソースをモデル化する必要があるかどうかを決定するルールは有効です。あなたの問題は間違った概念モデルに起因するものではありませんが、データベースモデルをRESTモデルにリークさせます。

概念的な観点から、employee関係内にのみ存在できる場合のcompanyは、 composition としてモデル化されます。したがって、employeeは、companyを介してのみ識別できます。これでデータベースが機能し、すべてのemployee行に一意の識別子が付与されます。

私のアドバイスは、インフラストラクチャの懸念をAPIにさらしているため、データベースモデルが概念モデルに漏れないようにすることです。たとえば、MongoDBのようなドキュメント指向データベースに切り替えて、会社のドキュメントの一部として従業員をモデル化でき、この人工的な一意のIDがなくなった場合、どうなりますか? APIを変更しますか?

追加の質問に答えるために

リソースが別のものに属しているという事実をどのように表すべきですか?

サブリソースを介した構成、URLリンクを介した他の関連付け。

リソースは他のリソースなしでは識別できないという事実をどのように表現すべきですか?

リソースURLで両方のid値を使用し、「組み合わせ」が存在するかどうかを確認して、データベースがAPIにリークしないようにしてください。

サブリソースとはどのような関係を意味し、モデル化することを意味しませんか?

サブリソースはコンポジションに適していますが、より一般的には、リソースは親リソースなしでは存在できず、常に1つの親リソースに属していることをモデル化するために話されています。あなたのルールwhen a resource could not exist without another, it should be represented as its sub-resourceは、この決定に対する適切なガイダンスです。

6
saintedlama

サブリソースが独自のエンティティなしで一意に識別可能な場合、サブリソースではなく、独自の名前空間(/ companies/{*}/users/{user}ではなく/ users/{user})が必要です。最も重要なのは、エンティティのデータベース主キーをリソース識別子として使用することは決してないということです。これは、実装の詳細が外部に漏れる最も一般的な間違いです。常に自然なビジネスキー(ユーザーIDや会社IDではなく、ユーザー名や会社番号など)を持っている必要があります。そのようなキーの一意性は、必要に応じて一意の制約によって強制できますが、エンティティのプライマリキーはアプリケーションの永続層から離れることはなく、少なくともサービスへの引数になることはありません方法。このルールを順守すれば、サブリソースにはないので、コンポジション(/ companies/{company}/users/{user})とアソシエーション(/ users/{user})を区別するのに問題はないはずです。グローバルなコンテキストでそれを識別する自然なビジネスキー、それが実際に依存するサブリソースであることを確信できます(または最初にビジネスキーを作成してグローバルに識別可能にする必要があります)。

4
Kai

これは、この状況を解決できる1つの方法です。

/ companies/{companyName}/employee/{employeeId}->従業員に関するデータを返します。個人のデータも含める必要があります

/ person/{peopleId}->個人に関するデータを返します

従業員の話は会社の話もせずに意味がありませんが、人の話は会社がなくても、複数の会社に雇われていても意味があります。人の存在は、会社に雇用されているかどうかには依存しませんが、雇用の存在は会社に依存します。

1
Lie Ryan

問題はないときのようです 明確な 会社ですが、従業員は技術的にはいくつかの会社または組織に属します。そうでなければ、彼らはお尻や政治家と呼ばれる可能性があります。従業員であることは、特定の関係ではなく、どこかで会社/組織の関係を意味します。また、従業員は複数の会社/組織で働くことができます。特定の企業コンテキストが必要な場合、元の作品/companies/{companyName}/users/{id}

使用するira/rsp/pensionのEmployerContributionを知りたいとします:/companies/enron/users/fred/EmployerContributionエンロンから寄付された特定の金額(または$ 0)を取得します。

Fred works(ed)の一部またはすべての会社からのEmployerContributionsが必要な場合はどうなりますか?
理にかなった具体的な会社は必要ありません。 /companies/any/employee/fred/EmployerContribution

「any」は、従業員の会社は関係ないが従業員であることは明らかな場合、明らかに抽象概念またはプレースホルダーです。 dbルックアップを防ぐには、「company」ハンドラーをインターセプトする必要があります(会社がキャッシュされない理由はわかりませんが、いくつあるのでしょうか?)

抽象化を変更して、Fredが過去10年間雇用されたすべての企業のようなものを表すこともできます。 /companies/last10years/employee/fred/EmployerContribution

0
bhatt