Overview
The jobs of a complex obs handler is to encapsulate all the business of handling its complex obs values. The API does not need to know the structure of the complex observation or how it is stored, since these tasks are the job of the complex obs handler. When a complex obs value is passed to the API to be stored, the API treats the value as a "black box" (i.e., contents unknown) and passes it along to the corresponding handler, which is responsible for return an Obs
that the API can store. The complexValue
of the Obs
is set by the handler. When a client asks the API for the value of a complex observation, the client must provide a "view" (i.e., the type of representation needed). The API passes the Obs
to the handler along with which view (representation) was requested and the handler is responsible for retrieving the complex value, rendering it appropriately for the requested view, and returning it. From the client's perspective, it can simply save and request complex values through the API; however, the API is actually just serving as a proxy for the complex obs handler responsible for that complex observation datatype. This design allows for all types of complex values to be added to the system, since all the actual management of the new type is done within the handler, which can be plugged into the system via modules.
Example
Let's say we want to introduce a new complex observation to support a "Whirligig" datatype. In other words, we want to be able to store "Whirligigs" in the patient's medical record as observations.
Create a Complex Obs Handler
First, we create a complex obs handler for our "Whirligig" datatype. As a complex obs handler, our WhirligigHandler
will need to implement the ComplexObsHandler interface. As part of that interface, the handler is responsible for:
Task | Description |
---|---|
Store/update Whirligigs
| Given an observation containing a Whirligig, store the Whirligig and place a reference to that Whirligig into the observation's valueComplex. |
Retrieve Whirligigs | Given an observation containing a reference to a Whirligig in its valueComplex and a requested view, load the actual Whirligig into the observation's ComplexData using a representation corresponding to the requested view. |
Purge Whirligigs | Given an observation containing a reference to a Whirligig in its valueComplex, ensure the corresponding Whirligig is purged (permanently deleted). |
Support different views | Report which views are supported for Whirligigs. All handlers should support the RAW view (i.e., return the Whirligig in it's native form); however, most handlers will be able to return alternative representations useful for different contexts. For example, our handler may be able to represent a Whirligig in HTML, as JSON data, and in a plain text format as well. |
Register the Handler
We create a module to add the capability for OpenMRS to store Whirligigs as observations. The module contains a reference to our WhirligigHandler
in its ModuleApplicationContext.xml
configuration, which is used by OpenMRS as the module is started.
Define Concept(s) with Whirligig Datatype
We create a concept for "Whirligig", setting it's datatype to complex and specifying our WhirligigHandler
as the handler for that concept.
Using Whirligigs
Now that you have a handler to manage Whirligigs and at least one concept in the system that is "answered" with a Whirligig, you can start using them. In practice, there are three key perspectives to consider: the client, the API, and the handler.
Participant | Description |
---|---|
Client | For the client, they can just send & receive Whirligig observations to the API. When creating a new observation, the client attaches a Whirligig to the Obs as ComplexData and submits it to the API. When retrieving Whirligig observations, the client just asks for the obs and find the Whirligig in the ComplexData . |
API | The API serves as a proxy to the handler. It has no idea what a Whirligig is, how to store them, or how to retrieve them. When the client submits an new Whirligig observation to be saved, the API notes it is complex and passes the observation to our Whirligig handler before saving it. When retrieving a Whirligig observation, the API passes it to the handler before returning it to the client, allowing the handler to attach the Whirligig. The API does support a valueComplex string on observations, but it is up to the handler to populate this field and, when retrieving or purging Whirligigs, to be able to parse what it stored in order to access the actual Whirligig for that observation. |
Handler | The handler doesn't need to interact directly with the client; in fact, as mentioned above, the client needn't know the handler exists. The handler just speaks to the API and performs all operations related to Whirligigs. When the client submits a Whirligig observation to the API, that observation is passed to the handler. At that point, the handler has two primary jobs: (1) store the Whirligig data and (2) place a reference to the Whirligig in the obs' valueComplex field. The handler will get this valueComplex reference back when the API needs to retrieve, update, or purge the Whirligig. When the client asks for an observation that contains a Whirligig, the API turns to the handler to fetch the actual Whirligig. The API passes the observation containing the reference (in valueComplex) and the handler is responsible for attaching the Whirligig to the observation. The handler may fetch the Whirligig from its own table, from a separate database, from a web service call, or (for small values) from within the valueComplex reference itself. |