[rest-practices] Modelling membership
Mark McLoughlin
markmc at redhat.com
Tue May 4 20:33:51 UTC 2010
On Tue, 2010-05-04 at 13:00 -0400, Bob McWhirter wrote:
> > So, by not having it toplevel, what we're thinking is equivalent to:
> >
> > POST /users/1234/memberships
> >
> > <membership>
> > <group id="4321"/>
> > </membership>
>
> Generally speaking, I'd rather see hrefs instead of IDs:
>
> <group href="${url_to_group}"/>
David and Eoghan had some discussion on this on irc the other day, and
we were observing:
- A href is an opaque identifier returned by the server, but not
guaranteed to remain the same over the long term - i.e. since the
server may be upgraded and the URI structure changed or, indeed,
the server might be moved
- A uuid, or similar, is generated by the server, but more useful
over the longer term - as long as you have the entry point URI, the
UUID and the object still exists, you should be able to trivially
get to it again
- Most resources will also have a human readable, user-generated
identifier too, but software clients should prefer the uuid
So, my thinking was:
- References/links in media returned by the server should use hrefs
- If a client wants to retain an identifier in its DB or whatever, it
should dereference the link and retain the uuid
- If the client needs to supply an identifier in some media it is
sending to the server, it should be able to use either a href or a
uuid (maybe the human readable id too?)
> I don't like this partial in-lined description of a <membership>
> resource, though, as then I have to keep up with user_id separately,
> it's not a part of the representation when fetched this way.
Note, what you've quoted above is what is POSTed by the client, whereas
the full description returned by the server does include a reference to
the user e.g.:
<membership>
<user href='/users/1234'/>
<group href='/groups/4321'/>
<link rel='self' href='/users/1234/memberships/1111'/>
</membership>
of course, that could be done with Atom links instead:
<membership>
<link rel='user' href='/users/1234'/>
<link rel='group' href='/groups/4321'/>
<link rel='self' href='/users/1234/memberships/1111'/>
</membership>
I like the consistency of that, but it starts to feel like overkill
pretty quickly
> It's
> easier to code if I know I can resolve a URL to fetch a complete
> object, instead of having to considering it within some context.
> Particularly if we're ignoring URL structure and being HATEOASishy.
>
> Depending on how "pure" you wanted to be, I might also do
>
> <memberships>
> <membership href="${url1}"/>
> <membership href="${url2}"/>
> </memberships>
>
> And treat it as a collection of references.
The same could be true of any collection, so I'd follow whatever pattern
we were using for other collections - which generally seems to be to
include the full representation rather than just a reference
> This is what I mean by a
> "top-level" object. One that is directly accessible with a discrete
> URL. Here, though, we look at N+1 fetches, or some extra way to
> inline the results or fetch-all in a single operation. (Both seem
> reasonable).
>
> Versus a "value" object, which is purely embedded within another
> object, and not directly referenceable.
>
> If you embed as a value, then I think you make sure it's not
> accessible, since it's more just an internal structure to the owning
> object.
I don't see why an embedded value shouldn't also be directly accessible,
it's quite common in collections AFAICS:
<collection>
<resource href="...">
<detail1/>
<detail2/>
</resource>
</collection>
> In this case, perhaps embedded value is the way to go, in which case,
> purely the following, where the only href/URL points to the referenced
> group:
>
> <memberships>
> <membership>
> <group href="${group_url}"/>
> </membership>
> </membership>
>
> Here, you don't have to repeat user_id, since context is implicit and
> mandatory, as we're embedded within the <user> object. But then we're
> back to the race condition of multiple updates to a single url to add/
> remove groups.
Wait, why are you now dropping the idea of having membership as a
distinct resource?
> But that is orthogonol, and affects all fields on all
> resources. We just happen to think it might be triggered on this one
> with high probability.
Agree on both counts
> Perhaps time to think about optimistic locking, and a <version> serial
> number or timestamp on all resources, to allow the server to perform
> reasonable optimistic locking.
Eoghan's "reject the update if it doesn't match this ETag" idea was
thinking along the same lines
> > We'll also make the membership list available read-only via the group:
> >
> > GET /groups/4321
> >
> > <group id="4321">
> > <name>foo</name>
> > ...
> > <memberships>
> > <link rel="membership" href="/users/1234/memberships/1111"/>
> > ...
> > </memberships>
> > </group>
>
> Though, does a group have memberships? Or does it have members? A
> "membership" seems to be a management/structural object from User
> perspective, but not necessarily from the group perspective,
> especially if it's just referencing it in a read-only fashion.
>
> I'd suggest:
>
> <group>
> <name>foo</name>
> <members>
> <user href="${user_url1}"/>
> <user href="${user_url2}"/>
> ..
> </members>
> </group>
>
> In this case, we're reducing the number of fetches to obtain a list of
> all members of a group. Having to bounce through a membership
> reference means 2N+1, where at least this way it's only N+1. Same
> inline/fetch-all notes from above may then apply to optimise some more.
That's all fair, except in our case:
- we're talking about an 'attachment' resource, which doesn't sound
as silly as a group having memberships
- we have some metadata associated with the attachment resource
itself, which you need some way to reference too
> What an amazingly deep topic. :)
I'm genuinely stunned at how REST seems like a bottomless pit of
circular debates about seemingly arbitrary choices :-)
Cheers,
Mark.
More information about the rest-practices
mailing list