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]>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]>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]> 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]>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<[email protected]?body=SIGNOFF%20openmrs-devel-l>from
>>>>  OpenMRS Developers' mailing list
>>>
>>>
>>> ------------------------------
>>> Click here to 
>>> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from 
>>> OpenMRS Developers' mailing list
>>
>> ------------------------------
>> Click here to 
>> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from 
>> OpenMRS Developers' mailing list
>>
>
> ------------------------------
> Click here to 
> unsubscribe<[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