当设计资源层次,一个时,应使用子资源?
我曾经相信,当资源不能没有另一个存在,它应该代表其子资源。 最近,我跑过这个反例:
- 一个员工在所有企业唯一标识。
- 员工的访问控制和生命周期取决于公司。
我模仿此为: /companies/{companyName}/employee/{employeeId}
请注意,我并不需要查找该公司为了找到员工,所以要这样呢? 如果我这样做,我付出的代价查找我不需要的信息。 如果我不这样做,这个URL错误地返回HTTP 200:
/companies/{nonExistingName}/employee/{existingId}
- 我应该如何代表一个资源属于另一个事实?
- 我应该如何代表一个事实,即资源不能没有相互识别呢?
- 在子资源意味着什么关系,并不意味着模型?
这是问题,因为它已经不再明显,用户属于一个特定的公司。
有时,这可能会突出显示与您的域模型的一个问题。 为什么一个用户属于一个公司? 如果我改变的公司,我是全新的人吗? 如果我对两家公司的工作是什么? 我是两个不同的人?
如果答案是肯定的,那么为什么不采取一些公司唯一标识符访问用户?
例如:用户名:
company/foo/user/bar
(这里bar
是我的用户名是唯一特定公司命名空间内)
如果答案是否定的,那么为什么我不是一个用户(人)的自己,以及company/users
收集只是指我: <link rel="user" uri="/user/1" />
注意:员工似乎更合适)
现在你的具体的例子之外,我认为,当涉及到使用 ,而不是所有权 (这就是为什么你与识别一个公司的隐含标识企业用户的冗余挣扎)资源子资源的关系是比较合适的。
我的意思这是一个users
实际上是一个企业资源的子资源,因为使用是定义一个企业与员工之间的关系-说的另一种方式是:你之前,你可以定义一个公司开始雇佣员工。 同样,用户(人)必须定义(出生)之前,你可以招募他们。
一年后,我用下面的妥协(对于包含一个唯一的标识符数据库行)结束:
- 分配所有资源的规范URI在根目录(例如
/companies/{id}
和/employees/{id}
- 如果资源不能没有另一个存在,它应该表现为它的子资源; 然而,治疗操作作为搜索引擎查询。 含义,而不是立即执行的操作,只需返回
HTTP 307 ("Temporary redirect")
在标准URI指向。 这将导致客户重复对标准URI的操作。 - 你的规范文档应该只露出满足您的概念模型(不依赖于实施细节),其根的资源。 实现细节可能会改变(你行可能不再是唯一可识别的),但你的概念模型将保持不变。 在上面的例子中,你会告诉客户
/companies
,但没有/employees
。
这种方法具有以下优点:
- 它消除了需要做无谓的数据库查找窗口。
- 它减少了完整性检查的数量为每一个请求。 至多,我要检查员工是否属于公司,但我再也不用为做两个验证检查
/companies/{companyId}/employees/{employeeId}/computers/{computerId}
。 - 它具有对数据库的可扩展性的混合影响。 一方面,你被锁定较少的表,对于较短的时间内减少锁争用。 但在另一方面,你都在增加,因为每一根资源必须使用不同的锁定顺序死锁的可能性。 我不知道这是否是一个净损益,但我乐意接受这样的事实数据库死锁无法阻止无论如何,所得锁定规则是容易理解和执行。 如果有疑问,选择简单。
- 我们的概念模型保持不变。 通过确保规范文档只公开我们的概念模型,我们可以自由地丢弃包含在未来实施细则的URI不会破坏现有的客户。 请记住,没有什么能阻止你在中间的URI暴露的实现细节,只要你规范声明其结构为未定义。
您的规则来决定,如果一个资源应该被建模为子资源是有效的。 不从错误的概念模型出现你的问题,但你让你泄露的数据库模型到您的REST模式。
从的概念图的employee
,如果它只能在存在于company
的关系被建模为组合物 。 该employee
可能因此只能通过鉴定company
。 现在数据库开始发挥作用,所有employee
行获得一个唯一的标识符。
我的建议是不要让数据库模型泄漏你的概念性的模型,因为你暴露基础设施问题,以你的API。 例如,当你决定要切换到像MongoDB的文档面向对象数据库在那里你可以作为公司文档的一部分,你的员工建模,不再有这种人为的唯一的ID,会发生什么? 你想改变你的API?
要回答你的问题外
我应该如何代表一个资源属于另一个事实?
通过URL链接通过子资源,其他协会组成。
我应该如何代表一个事实,即资源不能没有相互识别呢?
在资源URL同时使用ID值,并确保不要让你的数据库通过检查“组合”存在泄漏入您的API。
在子资源意味着什么关系,并不意味着模型?
子资源非常适合于成分,但更普遍地说,以模型,资源不能没有父资源存在,并永远属于一个母公司的资源。 您的规则when a resource could not exist without another, it should be represented as its sub-resource
是这一决定一个很好的指导。
如果一个子资源是唯一可识别的不拥有它的实体,它是没有子资源,应该有自己的名称空间(即/用户/用户{}而非/公司/ {*} /用户/用户{})。 最重要的是:永远不everer使用您的实体的数据库主密钥作为资源标识符。 这是最常见的错误,其执行细节泄漏到外面的世界。 你应该始终有一个自然的业务键(如用户名或公司数目,而不是用户ID或公司ID)。 在这样一个关键的独特性可以通过一个唯一约束强制执行,如果你愿意,而是一个实体的主键应该永远everer离开你的应用程序的持久层,或者至少不应该是一个参数的任何服务方法。 如果你去了这条规则,你不应该有成分区分任何麻烦(/公司/ {}公司/用户/用户{})和协会(/用户/用户{}),因为如果你的子资源没有自然业务键,标识它在全球范围内,你可以非常肯定这真的是一个根据子资源(或您必须首先创建一个业务键,使其在全球识别)。
这是你能解决这个问题的一种方法:
/公司/ {CompanyName}的/员工/ {}雇员 - 有关雇员>返回数据,还应该包括人员的数据
/人/ {的peopleid} - >返回关于人数据
谈到员工是没有意义也没有谈论公司,但讲的是人有一定道理,即使没有一个公司,甚至如果他被多家公司聘请。 一个人的存在是独立的是否他的任何公司录用,但劳动的存在不依赖于该公司。
这个问题似乎是在没有具体的公司,而是员工在技术上属于某个公司或组织,否则他们可以被称为烧伤或政治家。 作为一个员工的地方,但并不意味着公司/组织关系的特定的一个。 员工也可以连续工作超过一个公司/组织。 当需要特定的公司背景,然后你的原创作品/companies/{companyName}/users/{id}
比方说,你想知道EmployerContribution
为您的IRA / RSP /养老你会使用: /companies/enron/users/fred/EmployerContribution
你得到的具体数额贡献的安然公司(或$ 0)。
如果你想EmployerContribution
任何或所有公司弗雷德作品(ED)对于s?
你并不需要一个具体的公司为它是有意义的。 /companies/any/employee/fred/EmployerContribution
这里的“任何”显然是一个抽象或占位符的时候,员工的公司并不重要,但作为一个雇员做了。 你需要拦截“公司”处理程序,以防止数据库查询(虽然不知道为什么公司不会被缓存?有多少能有?)
你甚至可以改变抽象表示类似的弗雷德被使用在过去10年中的所有公司。 /companies/last10years/employee/fred/EmployerContribution