Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primary identifier missing #13

Closed
jhiemer opened this issue May 23, 2012 · 29 comments
Closed

Primary identifier missing #13

jhiemer opened this issue May 23, 2012 · 29 comments

Comments

@jhiemer
Copy link

jhiemer commented May 23, 2012

Hi Jon,
this is more a question than an issue report. What I was wondering about from the beginning on is, why the primary identifier in my case each Id of the entity is empty? In many client applications the primary identifier is important for application logic.

I did some short research, but I wasn't able to find some valuable information wether it make sense or not to include the primary id.

Could you explain it to me?

@jhiemer
Copy link
Author

jhiemer commented May 23, 2012

Jon,
any news on this topic?

@odrotbohm
Copy link
Member

In a REST context the URI is the identifier (hence the name Unique Resource Identifier). If there's a business key inside the entity on the domain class (like a customer id or the like) I wouldn't wanna use this as primary key actually. So we decided to keep the technical id out of the representation.

The primary reason is the following: if the representation contains the backend id of the entity - let's assume "1" - and you'd issue a PUT request to /entities/3, which entity would get updated? Would the entity with id 1 (as identified by the URI) get updated to get an id of 3? Should we silently ignore the id field? I think you get the point. Resource identification is done through URIs only.

@jhiemer
Copy link
Author

jhiemer commented May 25, 2012

Hi Oliver,
all right got that point. But what I don't understand is, why should id (as PK) and self entites/id differ? According to the code, the PK is always used to be included in the URI.

Besides that it is clear that the URI, is always the primary identifier but only for the REST stuff. In case of some application internal logic I would not want to use the whole URI at all. And additionally: as long as the user follows the HATEOAS principles and only uses the links for CRUD operations I don't see any disadvantage?

Sorry Oliver, I am still not confident with this.

@odrotbohm
Copy link
Member

A REST client does not know about a PK. It also doesn't know about it being encoded in the URI. Essentially that we use the PK in the URI is a URI design decision to make them readable easily. This comes with the side effect that to someone seeing the URI easily being able to get it's structure and generally being able to defer URIs to other resources. The crucial point is: a REST client is not aware of that. All it knows is that there's a rel of self which identifies the resource. Putting technical identifiers into the representation just create incentives to construct URIs manually.As I mentioned: it's perfectly fine to have business identifiers in the representation but that is defined in the media type. That must not interfere with the way the client and the server identify resources.

Yet another question to emphasize what I am shooting for: we also don't just add the PK of a related resource into the representation additionally so why would you with the PK of the resource you get the current representation for.

@jhiemer
Copy link
Author

jhiemer commented May 25, 2012

Perhaps we are talking past each other: But in my opinion the argumentation contradicts itself. First you say the REST client does not know about a PK and you just use them to make them easily readable, which means, you could have chosen/choose in future a different one. Following this argumentation, the REST client is not allowed to rely on a specific property for REST stuff as the primary key to build URIs manually. Using the primary key would in this case violate the architecture.

If you conclude this approach, id is just a normal property as all others are. Removing it makes it to a property with a special purpose and the PK. For me these are two different layers:

  • Layer One is application internal logic which might be based on the id property or whatever you might call it
  • Layer Two is the communication internal logic, receiving, save and deleting entities, only relying on the links.

Each of them should be independent of each other and should not add restrictions for cases, in which the user might possibly use it against all architectural principles.

@odrotbohm
Copy link
Member

If you conclude this approach, id is just a normal property as all others are. Removing it makes it to a property with a special purpose and the PK.

I think you're mixing up cause and effect here. A PK is some property artificially introduced to the server side model of a domain concept. Taking it a step back it's even as follows: you have domain concepts. Some of these concepts have a lifecycle which by definition makes them an entity. As you need to identify those to keep track of the lifecycle, you introduce an identifier. Now here's the trick: to make sure you can separate between actual domain properties and the ones introduced technical reason I'd recommend not to mixup business identifiers with technical ones.

So I think the following design approach is pretty consistent:

  • On the server side entities get a technical identifier to be able to be persisted into a store of that kind
  • On a REST interface - by definition - resources are identified by their URIs

Thus why would you expose the server side store identifier? This is actually leaking implementation details. You could argue it is as the PK is part of the URI already. But that's an orthogonal URI design decision which ultimately doesn't even have to be the case. You could expose customers via /customers/{customerId} where customerId is not the PK.

REST is not about CRUD on entities or exposing entities 1:1 to the web.

@jhiemer
Copy link
Author

jhiemer commented May 25, 2012

I think you're mixing up cause and effect here. A PK is some property artificially introduced to the server side model of a domain concept. Taking it a step back it's even as follows: you have domain concepts. Some of these concepts have a lifecycle which by definition makes them an entity.

I am aware of that. It was just used as placeholder for "resource identifier". And I do understand your argumentation, but unless I think it is not a matter of architecture than just more a matter of taste.

Thus why would you expose the server side store identifier?

There are two reasons:

  • You answered it yourself it is exposed anyway via URI
  • Extracting it from the URI into the identifier for each object is totally uncomfortable. No one likes to loop through all Links and search for Self to get the identifier just for client side comparison. That's not only uncomfortable but also increases execution time as you always need to get Self out of links.

Regarding my last post as well I need to correct myself:
What we face are actually three layers:

  • Server side (identified through PK of the entity model)
  • Communication side (identified through the URI)
  • Client side (now identified through the URI, but totally uncomfortable to use for client side logic with JavaScript for example)

As we have both arguments for and against it, I think the best way to handle this would be making the REST identifiers optionally included in the response body. By default it is turned off and if user likes to enable it, he needs to adjust some setting in the configuration.

@odrotbohm
Copy link
Member

  • Extracting it from the URI into the identifier for each object is totally uncomfortable. No one likes to loop through all Links and search for Self to get the identifier just for client side comparison. That's not only uncomfortable but also increases execution time as you always need to get Self out of links.

That's my point: you would not do that in any case as this assumes the client knows that the URI contains a different kind of identifier which it must not. So you'd either compare URIs or some arbitrary off-band identifier like a customer number.

As we have both arguments for and against it, I think the best way to handle this would be making the REST identifiers optionally included in the response body. By default it is turned off and if user likes to enable it, he needs to adjust some setting in the configuration.

That might be the case but I clearly vote a -1 on this one as it just breaks well-established principles in information hiding and pretty much breaks the abstraction purpose of a REST interface.

So I think we end up on agreeing to disagree on this one, which is perfectly fine :). Good reference discussion in any case!

@jhiemer
Copy link
Author

jhiemer commented May 25, 2012

That might be the case but I clearly vote a -1 on this one as it just breaks well-established principles in information hiding and pretty much breaks the abstraction purpose of a REST interface.

Ok I would vote + 1 and the rest could decide! :-)

So I think we end up on agreeing to disagree on this one, which is perfectly fine :). Good reference discussion in any case!

Indeed to both :-).

@manuelsantillan
Copy link

Since this would be really useful to integrate spring data rest with most client-side libs, those well-established principles seem a bit ivory-towerish to me ;-).

A huge +1 from my part. In fact, I've made it work by simply adding to org.springframework.data.rest.webmvc.EntityToResourceConverter a quick hack at the end of the convert method:

 @Override public Resource convert(Object source) {
...
  //hack to serialize the ID of the entity
    AttributeMetadata idAttribute=entityMetadata.idAttribute();
    if(idAttribute!=null){
        Object val= entityMetadata.idAttribute().get(source);
        Object name=idAttribute.name();
        if(val!=null && name!=null){
            entityDto.put(entityMetadata.idAttribute().name(),val);
        }
    }
   // END Hack  
    return new EntityResource(entityDto, links);

@odrotbohm
Copy link
Member

I'd argue this is implementing HTTP as it is specified. Of course, people also have built web applications piping non-idempotent operations through get request for ages and called that "pragmatic". Arguing that this was plain wrong can of course be considered "ivory tower", I'd call it "back to the spec". Same applies to IDs in representations: HTTP has a concept of an ID, it's called URI. If you build a remoting protocol on top of HTTP, why would you deliberately abuse it? You're not going to build a second SOAP, are you? ;)

However, no one is going to prevent you from hacking the feature into the library, simply don't expect us to advocate breaking the abstraction :). Let's agree to disagree and move on…

@jhiemer
Copy link
Author

jhiemer commented Oct 31, 2012

I knew this discussion would arise again. :-)

Nice solution Manuel, will perhaps use it as well!

@manuelsantillan
Copy link

By the way, how do you propose to resolve having access the entity ID without the rest of the URI on the client-side? URI parsing?

Would be nice to have a pet-store sample app that shows us how to "go back to the spec", for illustration purposes as well as to ensure that spring data rest is useful for the real world...

regards

@fgaujous
Copy link

Do you have some news about this issue ? Indeed, I want to make a javascript client side application and it would be very nice to have this id returned in the response to use hateoas Rest web service directly.
Thx

@jbrisbin
Copy link
Contributor

In the latest snapshots (and the upcoming version 1.1) you can configure whether or not you want an entity's ID exposed. The setting is not global, but it can be used (judiciously).

@fgaujous
Copy link

fgaujous commented Mar 1, 2013

Thank you, so I look forward to the next release. is there any release date sheduled ?

@deepakpol
Copy link

Thanks Oliver for pointing me to this discussion.
I think URI as an identifier is meant to enable communication over HTTP and should not intrude on internals of both server as well as client. Like the id is important for server as PK, clients also use it extensively as unique identifier and is an internal to the client implementation. How would you suggest client to identify the resource uniquely otherwise, for eg. ids to be used in html, for javascript logic? Of course client could generate its own identifier, but why bother since there is already a common way to handle it through ids? Hypermedia is supposed to guide the client in making valid state transfer requests on available resource, not hiding the id.
+1 and can I know when the next release with this flexibility would be available as mentioned in one of the posts?

@odrotbohm
Copy link
Member

@deepakpol: Just go ask yourself, what is the ID of the resource you fetched? That's what you use as id on the client side. Everything else is baking server side knowledge into the client, which you want to avoid. REST is not about Javascript (frameworks). I'd be incredibly hesitant to let requirements (especially that contradict or work around the underlying principles of your remoting technology) your JS library imposes into the design of your server side implementation / representation.

@fgaujous
Copy link

fgaujous commented May 8, 2013

I would say, I totally agree about the REST Hateoas principle about the URI as ID but, with JS framework like angular or BackBone, the id in the model client side drives the http request (POST,PUT..) to create or update model server side.
Furthermore, the rooting with # (hashbang mode) is more difficult without these ids (get #1].
Hiding the id doesn't help to develop RIA with these kind of frameworks, I wanted to use spring hateoas but, finally, I have created some REST WS just with Spring MVC to not add complexity in my project.
Maybe someone gets a successful experience with angular/backbone on Hateoas WS.

@jbrisbin
Copy link
Contributor

jbrisbin commented May 8, 2013

@deepakpol @fgaujous The latest SD REST 1.1 includes a configuration option in RepositoryRestConfiguration that allows you to expose the id value for a domain object. It's done on a case-by-case basis.

@fgaujous
Copy link

fgaujous commented May 8, 2013

@jbrisbin thank you, I will try it.

@jhiemer
Copy link
Author

jhiemer commented May 8, 2013

@fgaujous I must admit I had a long discussion with @olivergierke as well regarding the Ids, which I thought need to be exposed for client side frameworks. I am not able to tell you anything about Backbone, but I am using AngularJS for 6 months now. In my first trials had serious issues without the Ids. Then I created a service, which takes care about the Id and resource handling. And I must tell you I don't need any Id in the service.

@odrotbohm
Copy link
Member

@fgaujous Care to elaborate in how far its so terribly difficult for Angular/Backbone to work with URIs as IDs? Shouldn't it be totally opaque to the framework whether it uses a "1" or a "http://whatever.com/foo/1" or even an MD5 hash of the latter if you prefer to "hide" the URI structure and want to work with hex strings?

I think it's terribly amusing that your client side javascript framework defines how to reference data. :)

@jhiemer
Copy link
Author

jhiemer commented May 8, 2013

@olivergierke that's a matter of perspective. Others may see SD REST just as the data provider for the real product build in rock-solid Javascript. :-)

@fgaujous
Copy link

fgaujous commented May 8, 2013

@jhiemer Ok, Do you have somewhere an example with a service which takes care about the id and resource handling. ?
Concerning @olivergierke answer, what is the way you choose to manage it ? URIs as ids ?
Thank you.

@odrotbohm
Copy link
Member

@jhiemer - That's not the point. You've decided to use HTTP to access data, thus it's good practice to adhere to the concepts of it. And identity of resources is defined to be URIs in HTTP. You don't choose to ride by bike and complain about the missing steering wheel, do you?

@fgaujous - That shows the entire dilemma… it's not "URIs as ids", a URI is an id… Unique Resource Identifier… I think it can't be more to the point.

@scothis
Copy link

scothis commented May 8, 2013

The main issue here is third party client side libraries that assume the URL for a resources is basePath + identifier. There is a lot of code that bakes in this old assumption. I'll have to plead guilt as the RestStore in rest.js also follows this anti-pattern. Time to fix that...

@deepakpol
Copy link

@scothis If I talk about backbone (and pretty sure other libraries too), it does provide a default implementation with some assumptions about URL creation. However, I don't think that is totally correct and restrictive either. The framework is not restrictive and you can always use links embedded in the resource to transfer the states by overriding the behavior if the service provider supports hypermedia.
I understand the URL should be the unique identifier of the resource, but I don't think it has to be represented as "id" field, IMHO that is a some assumption which becomes restrictive to the server. I would say keeping URL to the resource in id field and as a 'self' link sounds redundant to me. If the client is going to use links to discover and follow next possible states, what use case would make them use the URL in id field? I think id should be considered as just another property of the representation and server should be allowed to transfer that

@jbrisbin thanks, I look forward to trying that out.

@scothis
Copy link

scothis commented May 9, 2013

@deepakpol library users will often take the path of least resistance, it's the lib's responsibility to make doing the right thing the easiest path.

I didn't mean to sugest that the URI should be stored in a property with the literal name 'id', but that the URI should be used as the identifier for the resource. Having the URI in a resource twice would indeed be redundant. Having a link with a rel self is sufficient. A good library should be configurable to know where to find the ID, whether it be a property named 'id' or a self link.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants