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
Polymorphic types #28
Comments
I think using a |
I agree with @mateomurphy; I think what you're looking for here is to make sure each transaction subtype's representation is an extension of a base "transaction" representation, then return them as is. A client that doesn't know (or care) about the subtypes can still view them as a list of transactions, ignoring the subtype extension fields it doesn't recognize. Although maybe a
|
👍 Using types as the actual key names can be difficult to work with in some languages. |
I'd vote emphatically against polymorphic types. Not every language supports them, and when they're not supported, you need to introspect the request to figure out what's going on and how to parse it. It's brutal. How I solved this, personally, is as follows:
It has been a pretty flexible solution for me thus far. |
@paddyforan - how are you handling a "running balance" of temporal objects? Are you leaving the sorting on the client side? That's, imo, a bad idea. |
I'm not entirely sure what you mean by a "a 'running balance' of temporal objects". Could you explain a bit more? And no, I do sorting on the server side, because otherwise the server would have to pass the entire body of data to the client when the client is really only interested in a slice of it. Which is bad. What I think you're asking is how to interleave debits and holds to get one list that is sorted and contains all sub-types. And in that case, I'd do the sort on the server-side, split them apart by type, pass them down to the client, then do the sort on the client-side and weave them back together. That's just hypothetical, though, because I've never found a situation in which I needed to sort over multiple types of resources at the same time. |
@paddyforan - apologies for the hasty comment. You're right, I definitely mean interleaving debits and holds to get one list and sorting them by created at time is a very common operation in a our API. This essentially gives our customers a timeline of activity on their accounts. This is a very common pattern, think of @facebook's timeline of events (photo, video, etc).
Are you suggesting pushing sorting logic into every client that consumes the API to construct this timeline? That seems quite counter-intuitive to me. |
Well, my first objection would be with the timeline example: I'd contend that those are all resources of the same type (Event, for example) that refer to other resources of a different type. This is, I believe, how Github's API is structured as well. Not polymorphic at all. And yes, the practice of sorting on the client side is not ideal. But I'd say the need to introspect a request to be able to decode it is even less ideal. If we can find a way to avoid both those things, I'm all for it. |
No, github's Activity API is polymorphic, and includes a
Other APIs I've used take a similar approach, and having written client code to access them, parsing them has not been an issue at all. |
Not to quibble, but looking at your example from github's API, I see a uniform stream item type with a related resource (the PushEvent) embedded rather than linked. Slightly different from a polymorphic type where all the attributes of the payload would be siblings of the attributes of the stream item. But I concede it's a pretty fine hair to split. :) |
I mostly agree with @lukfugl, and I'd interpret the Github sample as he did. But to get a bit "meta", really that bit of JSON doesn't inherently require one interpretation or the other: a client (or a person) could interpret the "type" field as either an attribute or a class name, and either could be right. Really, the same could be said for @mahmoudimus 's sample data as well, though it seems to imply a class-based representation more strongly. But in either case, you could interpret the "class" name as simply a qualifying string property. This reminds me of looking at the similarities and differences between different polymorphism implementations (strict single-inheritance, multiple-inheritance, interfaces/protocols, mixins...)--it's just turning the same core concept inside-out and back again: different classes of objects with some subset of common properties that can be interchanged in the right circumstances. Really, all of the different JSON examples here could be deserialized into any of those polymorphic paradigms. Nevertheless, I'm not sure what the best answer is here. So far, it appears the firm assumption is that the class of the serialized object should be able to be inferred solely from the JSON key in which it appears. If that is an overarching design decision, I don't see how--really--any polymorphism can be supported directly in the serialization format. You could work around it fairly easily for most purposes by having a uniform proxy object "wrap" each subtype when you needed to return them all in a single array, for example (as @lukfugl interpreted the Github data to intend). Then each proxy object could have a URL referencing the related resource--but you almost couldn't do this with an ID reference, because the ID alone wouldn't imply the subtype in which to find the ID (you could have a proxy object with a different property for each subtype and only populate one property, but now it's getting more complex). Maybe we need the type information in the yet-to-be-fleshed-out |
The 'type' key isn't meant to be a direct expression of your type hierarchy; exposing these kinds of details is brittle and fraught with problems. From http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
|
@steveklabnik Okay, all well and good...but then is all the by-convention direct inference of client-side classes from JSON key names in the existing Ember Data going to be removed, and we should expect it to change? According to your source, the current Ember Data convention would be considered harmful. |
The quote is about crossing the boundary of a client/server interaction. Once you've crossed the boundary, do whatever you want. The Ember feature you're talking about isn't contrary to my quote. |
... but expecting your Rails models to map exactly 100% always to your Ember ones would be. |
Agreed. Incidentally, I won't be using Rails in my backend...this discussion is primarily about the spec, not the reference implementation, right? Point being, I don't think the discussion was necessarily about explicitly mirroring the server side's class hierarchy into the client (resource model != data model and all that), It's about how the JSON sent (which does not necessarily reflect the underlying server-side object model) should imply mappings to classes in the client side. In that context, I fail to see how wanting to imply (to the client) a polymorphic class model for your resources is fundamentally different than the current convention, which implies which classes to instantiate on the client side 1-to-1? |
Yes, this is intended to be 100% agnostic on both ends. I mentioned Rails because you mentioned Ember, figured I'd make the example 100% concrete. On Thu, May 9, 2013 at 12:15 PM, SphtKr notifications@github.com wrote:
|
It looks like this ticket never came to any resolution. @mahmoudimus, how did you end up solving your problem? |
I already got hit by this. What I'm looking for is to accomodate the following structure in json-api terms:
GET /interfaces It will gaves a list of all system interface but with a normalized attributes (the common ones).
GET /interfaces/ethernets GET /interfaces/loopbacks According to above, would be a solution a 1-1 relationship between interfaces and the specific one: Interface <-> Ethernet, Interface <-> Loopback, and so on? |
At @balanced, we have an endpoint,
transactions
, that returns polymorphic types ofCredit
,Debit
,Hold
,Refund
, andReversal
. Thetransactions
endpoint returns these objects in order of creation.How would the payload structure look like?
Is this the right way?
The text was updated successfully, but these errors were encountered: