リソース階層を設計するとき、いつサブリソースを使用する必要がありますか?
私は、リソースが他のリソースなしでは存在できない場合、そのサブリソースとして表されるべきだと信じていました。私は最近この反例に出くわしました:
私はこれを次のようにモデル化しました:/companies/{companyName}/employee/{employeeId}
従業員を見つけるために会社を調べる必要はないので、注意してください。もしそうなら、私は必要のない情報を調べるために代金を払っています。そうしないと、このURLは誤ってHTTP 200を返します。
/companies/{nonExistingName}/employee/{existingId}
1年後、私は次の妥協で終了しました(一意の識別子を含むデータベース行の場合):
/companies/{id}
_および_/employees/{id}
_)。HTTP 307 ("Temporary redirect")
を返すだけです。これにより、クライアントは正規のURIに対して操作を繰り返します。/companies
_ではなく_/employees
_についてクライアントに伝えます。このアプローチには次の利点があります。
/companies/{companyId}/employees/{employeeId}/computers/{computerId}
_に対して2つの検証チェックを行う必要はなくなりました。ユーザーが特定の会社に属していることはもはや明らかではないため、これには問題があります。
これにより、ドメインモデルの問題が明らかになる場合があります。ユーザーが会社に属しているのはなぜですか?会社を変えた場合、まったく新しい人ですか? 2つの会社で働いている場合はどうなりますか?二人は違うの?
答えが「はい」の場合、ユーザーにアクセスするために会社固有の識別子を使用してみませんか?
例えばユーザー名:
company/foo/user/bar
(bar
は、特定の会社の名前空間内で一意のユーザー名です)
答えが「いいえ」の場合、なぜ私はユーザー(人)ではないのか、そしてcompany/users
コレクションは単に私を指しているだけです:<link rel="user" uri="/user/1" />
(注:従業員がより適切であると思われる)
あなたの特定の例の外では、リソースとリソースの関係は、所有権ではなくseになるとより適切であると思います(そしてそれが冗長性に苦しんでいる理由です暗黙的に会社を識別するユーザーの会社を識別する方法)。
これが意味するのは、users
は実際には会社のリソースのサブリソースであるということです。なぜなら、seは会社とその従業員の関係を定義するためです-別の言い方ですつまり、従業員の雇用を開始する前に会社を定義する必要があります。同様に、ユーザー(人)を募集する前に定義(生まれ)する必要があります。
サブリソースとしてリソースをモデル化する必要があるかどうかを決定するルールは有効です。あなたの問題は間違った概念モデルに起因するものではありませんが、データベースモデルを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
は、この決定に対する適切なガイダンスです。
サブリソースが独自のエンティティなしで一意に識別可能な場合、サブリソースではなく、独自の名前空間(/ companies/{*}/users/{user}ではなく/ users/{user})が必要です。最も重要なのは、エンティティのデータベース主キーをリソース識別子として使用することは決してないということです。これは、実装の詳細が外部に漏れる最も一般的な間違いです。常に自然なビジネスキー(ユーザーIDや会社IDではなく、ユーザー名や会社番号など)を持っている必要があります。そのようなキーの一意性は、必要に応じて一意の制約によって強制できますが、エンティティのプライマリキーはアプリケーションの永続層から離れることはなく、少なくともサービスへの引数になることはありません方法。このルールを順守すれば、サブリソースにはないので、コンポジション(/ companies/{company}/users/{user})とアソシエーション(/ users/{user})を区別するのに問題はないはずです。グローバルなコンテキストでそれを識別する自然なビジネスキー、それが実際に依存するサブリソースであることを確信できます(または最初にビジネスキーを作成してグローバルに識別可能にする必要があります)。
これは、この状況を解決できる1つの方法です。
/ companies/{companyName}/employee/{employeeId}->従業員に関するデータを返します。個人のデータも含める必要があります
/ person/{peopleId}->個人に関するデータを返します
従業員の話は会社の話もせずに意味がありませんが、人の話は会社がなくても、複数の会社に雇われていても意味があります。人の存在は、会社に雇用されているかどうかには依存しませんが、雇用の存在は会社に依存します。
問題はないときのようです 明確な 会社ですが、従業員は技術的にはいくつかの会社または組織に属します。そうでなければ、彼らはお尻や政治家と呼ばれる可能性があります。従業員であることは、特定の関係ではなく、どこかで会社/組織の関係を意味します。また、従業員は複数の会社/組織で働くことができます。特定の企業コンテキストが必要な場合、元の作品/companies/{companyName}/users/{id}
使用するira/rsp/pensionのEmployerContribution
を知りたいとします:/companies/enron/users/fred/EmployerContribution
エンロンから寄付された特定の金額(または$ 0)を取得します。
Fred works(ed)の一部またはすべての会社からのEmployerContribution
sが必要な場合はどうなりますか?
理にかなった具体的な会社は必要ありません。 /companies/any/employee/fred/EmployerContribution
「any」は、従業員の会社は関係ないが従業員であることは明らかな場合、明らかに抽象概念またはプレースホルダーです。 dbルックアップを防ぐには、「company」ハンドラーをインターセプトする必要があります(会社がキャッシュされない理由はわかりませんが、いくつあるのでしょうか?)
抽象化を変更して、Fredが過去10年間雇用されたすべての企業のようなものを表すこともできます。 /companies/last10years/employee/fred/EmployerContribution