There is a push in some corners to treat URIs as a means to represent hierarchies of objects. In this view, each path segment in the URI either represents either an object or a relationship to another object. For example, if a person object has a firstName, lastName and an address, under this model, the web service would provide the following URIs:
/person/{id}/firstName
/person/{id}/lastName
/person/{id}/address
So far so good. When you extend this to a case where a person resource has a number of address resources, it would lead to the following URIs:
/person/{id}/firstName
/person/{id}/lastName
/person/{id}/address/1/
/person/{id}/address/1/street
/person/{id}/address/1/city
/person/{id}/address/2/
/person/{id}/address/2/street
/person/{id}/address/2/city
Still good.
In the examples above, the structure of the representation is reflected in URIs.
Now, let's consider one more use case. Let's say, you want to expose the preferred address through a URI. This could be done with one of the following:
/person/{id}/preferredAddress
/person/{id}/address;type=prefered
/person/{id}/address?type=prefered
Still okay. But keep in mind that structure of a URI does not have to reflect the structure of a representation. In some cases, pedagogically designing URIs to reflect the structure of a representation could lead to overly verbose URIs. Take, for instance, a URI that needs to return an address that has not been used in the last six months. Is the URI going to be
/person/{id}/address;lastUsed=2007/10/01
or
/person/{id}/leastUsedAddress
Here, I would prefer the second form since it abstracts away the reasoning used to determine that a given address is least often used, and hence leaves room for future changes. Exposing the persistent state of data as URIs would over-expose implementation details, which incidentally is the same issue that bogged down most early EJB entity bean usage or Class to SOAP/WSDL mappings.
In general, my rules of thumb are follows:
- Design URIs to meaningfully expose resources
- Design representations to meet the data needs
- Don't mix the above two rules if you don't have to.
In particular, the third rule allows URIs to be independent of representations so that each can be evolved as needed independently of each other. If not followed, you may end up designing yet-another object query language built into URIs. Design URIs just way you would design public APIs, i.e. carefully, and amenable to change. In the same manner, you should be able to make compatible changes to your representations without necessarily changing URIs. URis and representations are two independent axes of an interface, and by folding them into a single axis, you are essentially losing a degree of freedom.
Nice post. I'm not a fan of the "object->URI" pattern and you've just scratched the surface on the challenges.
BTW - small thing, but I think of "representation" as the data sent over the wire (text/html, application/json, etc.) and the "resource" as the item described by the URI. In that light, your rule #2 might be better phrased as "Design resources to meet your data needs."
Or possibly I've missed something in the post?
Thanks for noticing the subtle point. I used "representation" to emphasize that each representation may need to be designed to meet whatever that needs to exposed for a given media type.
Yep. That makes sense. And I enjoy reading your posts.
I think like this:
You have some data.
You design a resource that exposes (some of) that data.
You design a URI that points to the resource.
You implement one or more media types to represent that resource.
Not a perfect story, but it gets me closer, anyway.