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]>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]>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]> 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]>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
>>>>
>>>
>>>
>> ------------------------------
>> 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]