[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