REST WS and subclasses

classic Classic list List threaded Threaded
19 messages Options
Darius Jazayeri-2 Darius Jazayeri-2
Reply | Threaded
Open this post in threaded view
|

REST WS and subclasses

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.

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.

[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-2 Darius Jazayeri-2
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list
Friedman, Roger (CDC/CGH/DGHA) (CTR) Friedman, Roger (CDC/CGH/DGHA) (CTR)
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

In reply to this post by Burke Mamlin

I actually don't mind this approach too much, I had already realized that multiple posts would be required to keep caches in synch, see last week's design notes (copied from 2 previous non-discussions).  My second proposal was intended as a way to do things with one post, by having everything go through the superclass. 

 

Saptarshi's noting the difference between resources and representations was really key for me.  We can get the availability of parent class fields we want by having a full representation that includes the full representations of  the parent class.  However, I think we need some additional representations to make things clearer for users (see RESTWS-225).

 

I'm concerned about one-step creation of subclass resources.  I'm pretty sure there's no other way to do it.  What concerns me is that the notation for subclass resources seems like it ought to spread to subresources as well.  That is, what we normally expect in our JSON for an object is a reference; what the notation for subclasses says is that we can replace the reference with JSON curly braces and define a parent class' values within them (including uuid, which is being set by subclass).  So when we define a person, why shouldn't we be able to substitute for a name in the list of person_names a pair of curly braces and define the person_name?  If we can, great; if we can't, can we make it work that way?  Then what happens when you have a many-to-many relationship with a relationship class?  Can we replace a reference in the list of related resources with curly braces and define it as well?  This is going to be totally recursive, so needs to be done right.

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Burke Mamlin
Sent: Tuesday, April 17, 2012 9:38 PM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] REST WS and subclasses

 

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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

In reply to this post by Darius Jazayeri-2
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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

Thanks for this excellent summary, Darius.  One caveat: if we find that we would really like a quick way to create patients & persons in a single step, we could always create one, whether it's an extra convenient POST method at patient/1234 or via a separate URL (e.g., like we do for importing HL7 messages & will probably have for CCDs, conveniently posting encounter forms, etc.).

-Burke

On Wed, Apr 18, 2012 at 9:13 PM, Darius Jazayeri <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Friedman, Roger (CDC/CGH/DGHA) (CTR) Friedman, Roger (CDC/CGH/DGHA) (CTR)
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

In reply to this post by Darius Jazayeri-3

I had put my notes from yesterday in RESTWS-221.  I will add Darius' and Burke's comments to that page.  As can be seen, there was not agreement on order/drugorder case.

 

Single resource per class routes all requests for subclasses through the class.  But when it comes time to create a resource, how does the system know which one to create?  It's going to require a discriminator, which was the aspect of my second approach which nobody at the meeting liked.

 

I think we have to decide what is the bigger evil: requiring multiple updates by the client or mucking around a lot with discriminators (I don't necessarily agree with Darius that more programming is better when all we're doing is saving the client one statement); also which will require more searching through annotations, producing a polymorphic representation from the superclass or finding the right subclass to perform a particular representation.

 

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Wednesday, April 18, 2012 9:14 PM
To: [hidden email]
Subject: Re: [OPENMRS-DEV] REST WS and subclasses

 

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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
sunbiz sunbiz
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

In reply to this post by Darius Jazayeri-3
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

My thinking behind #3-type-things is that you might want to be able to do both of these:
GET .../order -> all orders
GET .../order?t=org.openmrs.DrugOrder -> all drug orders

-Darius

On Thu, Apr 19, 2012 at 8:08 AM, Saptarshi Purkayastha <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

In reply to this post by sunbiz
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

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.

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Friedman, Roger (CDC/CGH/DGHA) (CTR) Friedman, Roger (CDC/CGH/DGHA) (CTR)
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM
To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

Can we remove "subclass" from any of this?  That term is not intuitive and has no clinical meaning – e.g., instead use something like registerOrderType or registerOrderClass, depending on how we decide to decide to model different types of orders.  If we follow the path laid out during the design call and start out just supporting different types as orderables using concept classes, then I'd favor registerOrderClass or registerOrderableClass.

-Burke

On Thu, Apr 19, 2012 at 1:29 PM, Darius Jazayeri <[hidden email]> wrote:
I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

From a code-writing perspective, it actually is important that it's a subclass. Particularly, this approach depends on the fact that we have an OrderService.getOrders(...) that returns a list of different kinds (subclasses) of orders.

I agree that we should not mention "subclass" anywhere in the externally-facing WS docs (and that's why I prefer this approach to the subclass-links-to-superclass approach: this way give us "different kinds of orders"). But I feel like it's important for the developer writing the code behind the scenes to know that subclasses are involved. Maybe this is just a javadoc issue.

-Darius


On Thu, Apr 19, 2012 at 11:20 AM, Burke Mamlin <[hidden email]> wrote:
Can we remove "subclass" from any of this?  That term is not intuitive and has no clinical meaning – e.g., instead use something like registerOrderType or registerOrderClass, depending on how we decide to decide to model different types of orders.  If we follow the path laid out during the design call and start out just supporting different types as orderables using concept classes, then I'd favor registerOrderClass or registerOrderableClass.

-Burke

On Thu, Apr 19, 2012 at 1:29 PM, Darius Jazayeri <[hidden email]> wrote:
I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Burke Mamlin Burke Mamlin
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

Sure, but don't you typically indicate the need for subclasses in Java by requiring an interface or specific base class as a parameter instead of putting the term "subclass" in your method name?  For example, assuming new types of orders will show up as new concept classes:

registerOrderableClass(Class<? extends Order> clazz, String name, OrderableClassHandler handler);

-Burke

On Thu, Apr 19, 2012 at 2:40 PM, Darius Jazayeri <[hidden email]> wrote:
From a code-writing perspective, it actually is important that it's a subclass. Particularly, this approach depends on the fact that we have an OrderService.getOrders(...) that returns a list of different kinds (subclasses) of orders.

I agree that we should not mention "subclass" anywhere in the externally-facing WS docs (and that's why I prefer this approach to the subclass-links-to-superclass approach: this way give us "different kinds of orders"). But I feel like it's important for the developer writing the code behind the scenes to know that subclasses are involved. Maybe this is just a javadoc issue.

-Darius


On Thu, Apr 19, 2012 at 11:20 AM, Burke Mamlin <[hidden email]> wrote:
Can we remove "subclass" from any of this?  That term is not intuitive and has no clinical meaning – e.g., instead use something like registerOrderType or registerOrderClass, depending on how we decide to decide to model different types of orders.  If we follow the path laid out during the design call and start out just supporting different types as orderables using concept classes, then I'd favor registerOrderClass or registerOrderableClass.

-Burke

On Thu, Apr 19, 2012 at 1:29 PM, Darius Jazayeri <[hidden email]> wrote:
I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

The issue that I neglected to mention is that I imagine these things will be spring beans, wired up that way, so there won't actually be a register method called at all...

You're right that we should be able to use generic type signatures to require and self-document the need for a subclass. I'll deal with that as an implementation detail. (And we're way in the weeds now...)

-Darius


On Thu, Apr 19, 2012 at 11:52 AM, Burke Mamlin <[hidden email]> wrote:
Sure, but don't you typically indicate the need for subclasses in Java by requiring an interface or specific base class as a parameter instead of putting the term "subclass" in your method name?  For example, assuming new types of orders will show up as new concept classes:

registerOrderableClass(Class<? extends Order> clazz, String name, OrderableClassHandler handler);

-Burke


On Thu, Apr 19, 2012 at 2:40 PM, Darius Jazayeri <[hidden email]> wrote:
From a code-writing perspective, it actually is important that it's a subclass. Particularly, this approach depends on the fact that we have an OrderService.getOrders(...) that returns a list of different kinds (subclasses) of orders.

I agree that we should not mention "subclass" anywhere in the externally-facing WS docs (and that's why I prefer this approach to the subclass-links-to-superclass approach: this way give us "different kinds of orders"). But I feel like it's important for the developer writing the code behind the scenes to know that subclasses are involved. Maybe this is just a javadoc issue.

-Darius


On Thu, Apr 19, 2012 at 11:20 AM, Burke Mamlin <[hidden email]> wrote:
Can we remove "subclass" from any of this?  That term is not intuitive and has no clinical meaning – e.g., instead use something like registerOrderType or registerOrderClass, depending on how we decide to decide to model different types of orders.  If we follow the path laid out during the design call and start out just supporting different types as orderables using concept classes, then I'd favor registerOrderClass or registerOrderableClass.

-Burke

On Thu, Apr 19, 2012 at 1:29 PM, Darius Jazayeri <[hidden email]> wrote:
I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list
Darius Jazayeri-3 Darius Jazayeri-3
Reply | Threaded
Open this post in threaded view
|

Re: REST WS and subclasses

Okay, it's the end of a very long workday, and I wanted to report that I have successfully implemented "Approach #3", i.e. the resource-per-class-hierarchy, and it works with Order/DrugOrder.

I need to do a refactoring pass tomorrow before I consider committing it, but preliminary comments are welcome:


-Darius

On Thu, Apr 19, 2012 at 1:25 PM, Darius Jazayeri <[hidden email]> wrote:
The issue that I neglected to mention is that I imagine these things will be spring beans, wired up that way, so there won't actually be a register method called at all...

You're right that we should be able to use generic type signatures to require and self-document the need for a subclass. I'll deal with that as an implementation detail. (And we're way in the weeds now...)

-Darius


On Thu, Apr 19, 2012 at 11:52 AM, Burke Mamlin <[hidden email]> wrote:
Sure, but don't you typically indicate the need for subclasses in Java by requiring an interface or specific base class as a parameter instead of putting the term "subclass" in your method name?  For example, assuming new types of orders will show up as new concept classes:

registerOrderableClass(Class<? extends Order> clazz, String name, OrderableClassHandler handler);

-Burke


On Thu, Apr 19, 2012 at 2:40 PM, Darius Jazayeri <[hidden email]> wrote:
From a code-writing perspective, it actually is important that it's a subclass. Particularly, this approach depends on the fact that we have an OrderService.getOrders(...) that returns a list of different kinds (subclasses) of orders.

I agree that we should not mention "subclass" anywhere in the externally-facing WS docs (and that's why I prefer this approach to the subclass-links-to-superclass approach: this way give us "different kinds of orders"). But I feel like it's important for the developer writing the code behind the scenes to know that subclasses are involved. Maybe this is just a javadoc issue.

-Darius


On Thu, Apr 19, 2012 at 11:20 AM, Burke Mamlin <[hidden email]> wrote:
Can we remove "subclass" from any of this?  That term is not intuitive and has no clinical meaning – e.g., instead use something like registerOrderType or registerOrderClass, depending on how we decide to decide to model different types of orders.  If we follow the path laid out during the design call and start out just supporting different types as orderables using concept classes, then I'd favor registerOrderClass or registerOrderableClass.

-Burke

On Thu, Apr 19, 2012 at 1:29 PM, Darius Jazayeri <[hidden email]> wrote:
I just replied on the ticket...

@Roger, I really really don't like the idea of ignoring extra properties. Trying to update a resource with a property it doesn't support should _not_ fail silently, it should give you a BAD REQUEST.

From my proposal above, I think we _should_ include a discriminator property when you're doing a POST to create, e.g:
{code}
{
    type: "org.openmrs.module.lab.LabModuleOrder",
    startDate: "2011-03-04",
    labModuleSpecimen: "<uuid>"
}
...will be delegated to the registered handler for LabModuleOrder, rather than being handled directly by OrderResource.
{code}

The one revision I'd make to what I said before is that we should hide the fully-qualified classnames from the end-user of REST. The module should do something like
{code}
registerSubclass(OrderResource.class, "LabModuleOrder", new LabModuleOrderSubclassHandler());
{code}

-Darius

On Thu, Apr 19, 2012 at 10:15 AM, Friedman, Roger (CDC/CGH/DGHA) (CTR) <[hidden email]> wrote:

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: [hidden email] [mailto:[hidden email]] On Behalf Of Darius Jazayeri
Sent: Thursday, April 19, 2012 1:01 PM


To: [hidden email]
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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> 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" <[hidden email]> 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 <[hidden email]> 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.

 

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.


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list

 


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list


[hidden email] from OpenMRS Developers' mailing list



[hidden email] from OpenMRS Developers' mailing list