That's why I'd like to see subclasses appear as "flat"; you'd still have to 
update up the tree, but you could use the same body and each layer would pick 
off the variables it needs.  As for create, Hibernate would take care of the 
splitting and only the subclass would be exposed, so there's no need for 
multiple updates.  But this means that there would be no errors for specifying 
"extra" variables.

From: [email protected] [mailto:[email protected]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM
To: [email protected]
Subject: Re: [OPENMRS-DEV] REST WS and subclasses

Burke, if we incorporate Jeff Wishnie's off-list suggestion to us about making 
the "Subclass links to Superclass" also include a link from the superclass to 
the subclass, we could do something like this:

GET order ->
[
    {
        // order properties here
        links: [
            { rel: "self": uri: "order/<uuid>" },
            { rel: "more-details": uri: "drugorder/<uuid>" }
        ]
    },
    {
        // order properties here
        links: [
            { rel: "self": uri: "order/<uuid2>" },
            { rel: "more-details": uri: "laborder/<uuid2>" }
        ]
    }
]

GET drugorder ->
{
    // only DrugOrder-specific properties here
    order: { ref representation of the superclass },
    links: [
        { rel: "self": uri: "drugorder/<uuid>" }
    ]
}

Personally I prefer my originally-proposed approach #3 instead of this, though. 
It seems like it would be easier for clients to put all a DrugOrder properties 
(own and inherited) in a single document, rather than having dosage in one 
document and startDate and concept in a linked document. As I phrased it 
before, it makes sense to me that if subclasses "inherit the meaning of the 
superclass, not just its implementation details" then it makes sense to expose 
them all through a single resource...

(But I'm no REST expert, so...)

-Darius


On Thu, Apr 19, 2012 at 9:29 AM, Burke Mamlin 
<[email protected]<mailto:[email protected]>> wrote:
Orders are often approached generically (e.g. getting a list of active orders; 
creating order sets that contain meds, tests, educational materials, referrals, 
etc.; etc.), all orders share a base of properties & workflows, and we want to 
allow for modules to add additional types.  By treating all orders types as 
"orders", we allow consumers to treat different types differently when they 
know about them, but also fall back to treating them as generic orders for 
those types they don't understand.

For example, if I want to generate a list of all active orders, I can go to one 
place, get this list, and show the names of them, who ordered them, etc. rather 
than fetching the active list from each resource (drugorder, laborder, 
radiologyorder, nursingorder, referralorder, dietorder, activityorder, 
precautionorder, and callorder) and then be forced to refactor my code when I 
want to include Roger's latest whirlygigorder.

We could model all order types as separate resources, if we can still have a 
centralized mechanism for dealing with orders (e.g., fetching all active 
orders) and ensure that all of these resources have some shared part of their 
representation that was guaranteed to not only have the same properties, but 
follow the same business logic (e.g., all orders must have an orderer, the 
definition of "active" needs to be applied the same across all orders, etc.)

-Burke

On Thu, Apr 19, 2012 at 11:08 AM, Saptarshi Purkayastha 
<[email protected]<mailto:[email protected]>> wrote:
The Approach#3 is extremely complex and means that someone needs to understand 
the type of "what"

A client does not have to understand the types, just resources and so to me the 
approach #1 is generalizable and concise to understand as a general pattern 
when having to communicate with OpenMRS web services. Again why would it be a 
problem to use Approach#1 with drugorder or laborder resource??

---
Regards,
Saptarshi PURKAYASTHA

My Tech Blog:  http://sunnytalkstech.blogspot.com
You Live by CHOICE, Not by CHANCE


On 19 April 2012 06:43, Darius Jazayeri 
<[email protected]<mailto:djazayeri%[email protected]>> wrote:
Hi All,

We had a very good design call discussion on this, and Burke and I got some 
brief, dense, and helpful insight on this from Jim Webber.

The key insight: "Dealing with inheritance is easy: REST doesn't do it." 
Basically: think of REST resources as simply exchanging documents. There can be 
links between documents, but they are not allowed to have any formal type 
hierarchy.

So, after wrapping our heads around that, we've come up with three different 
approaches to hide inheritance in the REST API, each applicable to different 
things in the underlying Java class model:

Approach #1 - Subclass links to Superclass
Used for: Person/Patient
In the REST API, the subclass document contains only subclass-specific 
attributes, as well as a link to the superclass document. Example:
GET patient/1234 ->
{
    identifiers: [ ... ],
    person: { ref representation of person/1234 }
}

Even though the underlying Patient object inherits from Person, the 
PatientResource should hide this. Thus you cannot edit a birthdate via the 
patient resource, and creating a patient requires two POSTs (one to create a 
person resource, the next to create a patient resource that links to the person 
resource).

Approach #2 - Hide the Superclass
Used for: ActiveListItem/Allergy/Problem
We use this approach when the multiple subclasses are not meaningfully related 
to each other, and they only share a superclass for ease of implementation. The 
REST API has no resource for the superclass. Each subclass is its own resource, 
which contains the union of the properties of its superclass and itself.

Approach #3 - Single Resource for Class Hierarchy
Used for: Concept/ConceptNumeric/ConceptComplex
Used for: Order/DrugOrder/XyzModuleOrder
We use this approach when multiple subclasses really are different versions of 
the same basic thing. In other words, they inherit the meaning of the 
superclass, not just its implementation details.
The REST API has a single resource (Concept, Order) that manages the documents 
for all subclasses. Each document represents an underlying instance of some 
class in the hierarchy, and it contains all properties of that class, including 
inherited ones.
For example, GET order?patient=1234 ->
[
    {
        type: "org.openmrs.DrugOrder",
        startDate: "2011-01-01",
        // other Order properties go here
        dosage: "100mg",
        // other DrugOrder properties go here
        links: [ { rel: "self", uri: "order/11111" } ]
    },
    {
        type: "org.openmrs.Order",
        startDate: "2011-02-03",
        // other Order properties go here
        links: [ { rel: "self", uri: "order/22222" } ]
    },
    {
        type: "org.openmrs.module.lab.LabModuleOrder",
        startDate: "2011-03-04",
        // other Order properties go here
        specimen: { ref representation of a lab specimen },
        // other LabModuleOrder properties go here
        links: [ { rel: "self", uri: "order/33333" } ]
    }
]

I've put a discriminator field in here ("type" might not be a safe name) 
because it seems quite useful, and I think it's necessary for object creation.
For example: POST order
{
        type: "org.openmrs.module.lab.LabModuleOrder",
        startDate: "2011-03-04"
}
...will be delegated to the registered handler for LabModuleOrder, rather than 
being handled directly by OrderResource.

Implementation-wise:

  *   we are going to count on the underlying OpenMRS API (and ultimately 
Hibernate) to work such that if we do OrderService.getOrdersByPatient(1234) we 
get back a List<Order> whose individual items are actually of their correct 
subclasses.
  *   the core RESTWS module, and other modules, need to register handlers for 
their known subclasses
  *   we will have to write some code, but that's the fun part. :-)
Thoughts?

-Darius


On Wed, Apr 18, 2012 at 6:35 AM, Burke Mamlin 
<[email protected]<mailto:[email protected]>> wrote:
My biggest concern is that it requires that consumers of the API know/learn our 
data model; however, since the person is presented as a property of the patient 
and gender as a property of the person (not the patient directly), it's about 
as good a solution as I can imagine.

-Burke

On Tue, Apr 17, 2012 at 10:42 PM, Darius Jazayeri 
<[email protected]<mailto:[email protected]>> wrote:

Yes, I meant what you said Burke.

(Hopefully that was my only typo.)

-Darius (by phone)
On Apr 17, 2012 6:48 PM, "Burke Mamlin" 
<[email protected]<mailto:[email protected]>> wrote:
Darius,

Did you mean to two posts, one to patient & the other to person?  Both of yours 
were to the same resource.

This implies that if you want to modify a patient's gender and identifiers, you 
have to do two POSTs.
For example:
POST patient/abcd1234 { identifiers: [ ... ] }
POST person/abcd1234 { gender: 'M' }

Cheers,

-Burke

On Tue, Apr 17, 2012 at 8:01 PM, Darius Jazayeri 
<[email protected]<mailto:[email protected]>> wrote:
Hi All,

On tomorrow's design call one topic we will discuss is how to properly 
represent inheritance and subclasses in a RESTful way. Fun and exciting 
background discussion can be found on the ticket: 
https://tickets.openmrs.org/browse/RESTWS-221. Call-in details are 
here<https://tickets.openmrs.org/browse/RESTWS-221>.

My proposal, generally supported by Saptarshi, and disliked by Roger, is that 
we represent a subclass as basically the composition of a superclass resource, 
and a subclass resource contains subclass-specific properties, and a pointer to 
the superclass.

For example: GET patient/abcd1234 ->
{
    identifiers: [ ... ], // this is the only Patient-specific property
    links: [
        { rel: "self", uri: "patient/abcd1234" }
    ],
    person: { // this is a pointer to the superclass
        names: [ ... ],
        gender: 'M',
        // other properties on the Person superclass follow
        links: [
            { rel: "self", uri: "person/abcd1234" }
        ]
    }
}

This implies that if you want to modify a patient's gender and identifiers, you 
have to do two POSTs.
For example:
POST patient/abcd1234 { identifiers: [ ... ] }
POST patient/abcd1234 { gender: 'M' }

You should be able to create a patient in a single POST, but not update one 
that way.

At first this seems inconvenient, and unintuitive for someone who's used to the 
OpenMRS Java API. The reason for this is that I think it's necessary to support 
web-standard caching, which allows web service scalability. Basically, imagine 
that someone may be running a reverse-proxy on their server, which caches 
resources generated by the server and serves them up to many web clients, 
relieving server load. In order for that reverse-proxy cache to avoid serving 
up stale data, we cannot allow doing POST patient/abc123 to modify the resource 
at person/abc123. (According to web standards, if the cache sees a POST to 
patient/abc123, this invalidates that specific cache entry, but all of this is 
invisible to the server.) Thus my proposal.

I'm only moderately certain I'm approaching this right, so if you know or 
suspect the right answer to this problem (especially if it's different from my 
proposal), please reply and/or join us on the design call tomorrow!

-Darius

PS- The other topic we'll discuss on the call is Wyclif's proposal for a 
module, that will allow us to reboot our implementation of orders and order 
entry, such that we implement something better, and it runs on both old and new 
versions of OpenMRS. All in all this will be an action-packed call.
________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list

________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list
________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list

________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list


________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list

________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list

________________________________
Click here to 
unsubscribe<mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l> 
from OpenMRS Developers' mailing list

_________________________________________

To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to 
[email protected] with "SIGNOFF openmrs-devel-l" in the  body (not 
the subject) of your e-mail.

[mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]

Reply via email to