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 <[email protected]>wrote: > Hi All, > > We had a very good design call discussion on this, and Burke and I got > some brief, dense, and helpful insight on this from Jim Webber. > > The key insight: "Dealing with inheritance is easy: REST doesn't do it." > Basically: think of REST resources as simply exchanging documents. There > can be links between documents, but they are not allowed to have any formal > type hierarchy. > > So, after wrapping our heads around that, we've come up with three > different approaches to hide inheritance in the REST API, each applicable > to different things in the underlying Java class model: > > *Approach #1 - Subclass links to Superclass* > *Used for: Person/Patient* > In the REST API, the subclass document contains only subclass-specific > attributes, as well as a link to the superclass document. Example: > GET patient/1234 -> > { > identifiers: [ ... ], > person: { ref representation of person/1234 } > } > > Even though the underlying Patient object inherits from Person, the > PatientResource should hide this. Thus you cannot edit a birthdate via the > patient resource, *and* creating a patient requires two POSTs (one to > create a person resource, the next to create a patient resource that links > to the person resource). > > *Approach #2 - Hide the Superclass* > *Used for: ActiveListItem/Allergy/Problem* > We use this approach when the multiple subclasses are not meaningfully > related to each other, and they only share a superclass for ease of > implementation. The REST API has no resource for the superclass. Each > subclass is its own resource, which contains the union of the properties of > its superclass and itself. > > *Approach #3 - Single Resource for Class Hierarchy* > *Used for: Concept/ConceptNumeric/ConceptComplex* > *Used for: Order/DrugOrder/XyzModuleOrder* > We use this approach when multiple subclasses really are different > versions of the same basic thing. In other words, they inherit the meaning > of the superclass, not just its implementation details. > The REST API has a single resource (Concept, Order) that manages the > documents for all subclasses. Each document represents an underlying > instance of some class in the hierarchy, and it contains all properties of > that class, including inherited ones. > For example, GET order?patient=1234 -> > [ > { > type: "org.openmrs.DrugOrder", > startDate: "2011-01-01", > // other Order properties go here > dosage: "100mg", > // other DrugOrder properties go here > links: [ { rel: "self", uri: "order/11111" } ] > }, > { > type: "org.openmrs.Order", > startDate: "2011-02-03", > // other Order properties go here > links: [ { rel: "self", uri: "order/22222" } ] > }, > { > type: "org.openmrs.module.lab.LabModuleOrder", > startDate: "2011-03-04", > // other Order properties go here > specimen: { ref representation of a lab specimen }, > // other LabModuleOrder properties go here > links: [ { rel: "self", uri: "order/33333" } ] > } > ] > > I've put a discriminator field in here ("type" might not be a safe name) > because it seems quite useful, and I think it's necessary for object > creation. > For example: POST order > { > type: "org.openmrs.module.lab.LabModuleOrder", > startDate: "2011-03-04" > } > ...will be delegated to the registered handler for LabModuleOrder, rather > than being handled directly by OrderResource. > > Implementation-wise: > > - we are going to count on the underlying OpenMRS API (and ultimately > Hibernate) to work such that if we do OrderService.getOrdersByPatient(1234) > we get back a List<Order> whose individual items are actually of their > correct subclasses. > - the core RESTWS module, and other modules, need to register handlers > for their known subclasses > - we will have to write some code, but that's the fun part. :-) > > Thoughts? > > -Darius > > > On Wed, Apr 18, 2012 at 6:35 AM, Burke Mamlin <[email protected]>wrote: > >> My biggest concern is that it requires that consumers of the API >> know/learn our data model; however, since the person is presented as a >> property of the patient and gender as a property of the person (not the >> patient directly), it's about as good a solution as I can imagine. >> >> -Burke >> >> >> On Tue, Apr 17, 2012 at 10:42 PM, Darius Jazayeri <[email protected]>wrote: >> >>> Yes, I meant what you said Burke. >>> >>> (Hopefully that was my only typo.) >>> >>> -Darius (by phone) >>> On Apr 17, 2012 6:48 PM, "Burke Mamlin" <[email protected]> wrote: >>> >>>> Darius, >>>> >>>> Did you mean to two posts, one to patient & the other to person? Both >>>> of yours were to the same resource. >>>> >>>> This implies that if you want to modify a patient's gender *and* >>>> identifiers, >>>>> you have to do *two* POSTs. >>>>> For example: >>>>> POST patient/abcd1234 { identifiers: [ ... ] } >>>>> POST *person*/abcd1234 { gender: 'M' } >>>> >>>> >>>> Cheers, >>>> >>>> -Burke >>>> >>>> On Tue, Apr 17, 2012 at 8:01 PM, Darius Jazayeri >>>> <[email protected]>wrote: >>>> >>>>> Hi All, >>>>> >>>>> On tomorrow's design call one topic we will discuss is how to properly >>>>> represent inheritance and subclasses in a RESTful way. Fun and exciting >>>>> background discussion can be found on the ticket: >>>>> https://tickets.openmrs.org/browse/RESTWS-221. Call-in details are >>>>> here <https://tickets.openmrs.org/browse/RESTWS-221>. >>>>> >>>>> My proposal, generally supported by Saptarshi, and disliked by Roger, >>>>> is that we represent a subclass as basically the composition of a >>>>> superclass resource, and a subclass resource contains subclass-specific >>>>> properties, and a pointer to the superclass. >>>>> >>>>> For example: GET patient/abcd1234 -> >>>>> { >>>>> identifiers: [ ... ], // this is the only Patient-specific property >>>>> links: [ >>>>> { rel: "self", uri: "patient/abcd1234" } >>>>> ], >>>>> person: { // this is a pointer to the superclass >>>>> names: [ ... ], >>>>> gender: 'M', >>>>> // other properties on the Person superclass follow >>>>> links: [ >>>>> { rel: "self", uri: "person/abcd1234" } >>>>> ] >>>>> } >>>>> } >>>>> >>>>> This implies that if you want to modify a patient's gender *and* >>>>> identifiers, >>>>> you have to do *two* POSTs. >>>>> For example: >>>>> POST patient/abcd1234 { identifiers: [ ... ] } >>>>> POST patient/abcd1234 { gender: 'M' } >>>>> >>>>> You should be able to *create* a patient in a single POST, but not >>>>> update one that way. >>>>> >>>>> At first this seems inconvenient, and unintuitive for someone who's >>>>> used to the OpenMRS Java API. The reason for this is that I think it's >>>>> necessary to support web-standard caching, which allows web service >>>>> scalability. Basically, imagine that someone may be running a >>>>> reverse-proxy >>>>> on their server, which caches resources generated by the server and serves >>>>> them up to many web clients, relieving server load. In order for that >>>>> reverse-proxy cache to avoid serving up stale data, we cannot allow doing >>>>> POST patient/abc123 to modify the resource at person/abc123. (According to >>>>> web standards, if the cache sees a POST to patient/abc123, this >>>>> invalidates >>>>> that specific cache entry, but all of this is invisible to the >>>>> server.) Thus my proposal. >>>>> >>>>> I'm only moderately certain I'm approaching this right, so if you know >>>>> or suspect the right answer to this problem (especially if it's different >>>>> from my proposal), please reply and/or join us on the design call >>>>> tomorrow! >>>>> >>>>> -Darius >>>>> >>>>> PS- The other topic we'll discuss on the call is Wyclif's proposal for >>>>> a module, that will allow us to reboot our implementation of orders and >>>>> order entry, such that we implement something better, and it runs on both >>>>> old and new versions of OpenMRS. All in all this will be an action-packed >>>>> call. >>>>> ------------------------------ >>>>> Click here to >>>>> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from >>>>> OpenMRS Developers' mailing list >>>> >>>> >>>> ------------------------------ >>>> Click here to >>>> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from >>>> OpenMRS Developers' mailing list >>> >>> ------------------------------ >>> Click here to >>> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from >>> OpenMRS Developers' mailing list >>> >> >> ------------------------------ >> Click here to >> unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from >> OpenMRS Developers' mailing list >> > > ------------------------------ > Click here to > unsubscribe<[email protected]?body=SIGNOFF%20openmrs-devel-l>from > OpenMRS Developers' mailing list > _________________________________________ To unsubscribe from OpenMRS Developers' mailing list, send an e-mail to [email protected] with "SIGNOFF openmrs-devel-l" in the body (not the subject) of your e-mail. [mailto:[email protected]?body=SIGNOFF%20openmrs-devel-l]

