...
What
...
is
...
OpenMRS
...
OpenMRS
...
is
...
an
...
electronic
...
medical
...
record
...
platform.
...
Basically
...
we
...
provide
...
an
...
API
...
and
...
data
...
model
...
for
...
storing
...
and
...
analyzing
...
patient-level
...
data,
...
and
...
a
...
reference
...
application
...
that's
...
used
...
in
...
40
...
+
...
mostly-developing
...
countries.
...
The
...
system
...
includes
...
"core"
...
code
...
and
...
pluggable
...
"modules"
...
that
...
allow
...
very
...
powerful
...
extensions
...
of
...
functionality,
...
and
...
UI-level
...
customization.
...
What
...
about
...
Web
...
Services
...
In
...
the
...
past,
...
people
...
have
...
written
...
several
...
web
...
service
...
modules
...
for
...
OpenMRS,
...
but
...
they've
...
never
...
had
...
enough
...
high-level
...
design.
...
Now,
...
for
...
the
...
first
...
time,
...
we're
...
really
...
dedicating
...
our
...
core
...
development
...
team
...
to
...
doing
...
web
...
services
...
right.
...
We
...
need
...
to
...
build
...
a
...
module
...
that
...
exposes
...
our
...
API
...
over
...
web
...
services,
...
in
...
a
...
way
...
that
...
we
...
will
...
support
...
going
...
forwards,
...
and
...
with
...
a
...
design
...
we're
...
proud
...
of.
...
Version
...
1
...
Requirements
...
We
...
polled
...
the
...
broader
...
OpenMRS
...
community,
...
and
...
came
...
up
...
with
...
a
...
list of User Stories to include in Version 1 of the module. Very concisely they are:
- Get a "patient summary" (i.e.
...
- a
...
- big
...
- chunk
...
- of
...
- data
...
- about
...
- a
...
- patient,
...
- their
...
- clinical
...
- encounters,
...
- etc)
...
- Create
...
- a
...
- new
...
- encounter
...
- or
...
- edit
...
- an
...
- existing
...
- encounter
...
- for
...
- a
...
- patient
...
- (e.g.
...
- for
...
- mobile
...
- data
...
- entry)
...
- Manage
...
- and
...
- list
...
- a
...
- named
...
- group
...
- of
...
- patients
...
- (to
...
- support
...
- downloading
...
- summaries
...
- of
...
- "My
...
- patients"
...
- to
...
- a
...
- phone)
...
- Let
...
- an
...
- external
...
- laboratory
...
- system
...
- look
...
- up
...
- patients
...
- and
...
- metadata
...
- in
...
- OpenMRS
...
- (e.g.
...
- the
...
- lab
...
- system
...
- fetches
...
- a
...
- patient,
...
- location,
...
- and
...
- user
...
- so
...
- that
...
- it
...
- can
...
- create
...
- an
...
- encounter
...
- in
...
- OpenMRS)
...
- Migrate
...
- data
...
- from
...
- legacy
...
- systems
...
Design
...
Thoughts
...
and
...
Questions
...
The
...
API
...
we
...
are
...
going
...
to
...
expose
...
in
...
our
...
1.0
...
pass
...
at
...
web
...
services
...
is
...
entirely
...
about
...
doing
...
CRUD
...
on
...
a
...
data
...
store.
...
We're
...
not
...
dealing
...
with
...
any
...
application
...
flow
...
or
...
business
...
logic.
...
As
...
such
...
we
...
will
...
aim
...
for
...
level
...
2
...
of
...
the
...
...
...
Model (described by Martin Fowler here) and ignore the idea of embedding state transitions in our resources. Is this a mistake?
We will allow Spring to automatically transform our results to json or xml depending on the HTTP request. (This probably prevents us from having a proper DAP or XSD. Is this a mistake?)
We'll be following (mostly) standard practice with HTTP methods for domain object CRUD:
- Create: POST /ws/rest/patient
- Search: GET /ws/rest/patient?q=name+or+identifier
...
- Retrieve:
...
- GET
...
- /ws/rest/patient/
...
- uuid
...
- Update:
...
- POST
...
- /ws/rest/patient/
...
- uuid with the request content the properties we want to change on the object
- e.g.
...
- to
...
- change
...
- a
...
- patient's
...
- birthdate
...
- you'd
...
- POST
...
- {
...
- "birthdate":
...
- "1978-05-24"
...
- }
...
- does
...
- this
...
- seem
...
- okay,
...
- rather
...
- than
...
- using
...
- PUT
...
- with
...
- the
...
- complete
...
- object?
...
- Delete:
...
- DELETE
...
- /ws/rest/patient/
...
- uuid
...
- this
...
- actually
...
- means
...
- "mark
...
- deleted"
...
- in
...
- OpenMRS
...
- "Delete
...
- forever":
...
- DELETE
...
- /ws/rest/patient/
...
- uuid
...
- ?purge=true
...
- actually
...
- deletes
...
- an
...
- entity
...
- from
...
- the
...
- database
...
- is
...
- there
...
- a
...
- better
...
- way
...
- to
...
- indicate
...
- this
...
- special,
...
- rarely-used
...
- method?
...
Our
...
domain
...
objects
...
are
...
often
...
large,
...
and
...
contain
...
lots
...
of
...
rarely-interesting
...
book-keeping
...
information
...
(creator,
...
date
...
created,
...
etc),
...
and
...
we
...
expect
...
many
...
clients
...
to
...
be
...
bandwidth-limited.
...
Therefore
...
we
...
want
...
to
...
allow
...
clients
...
to
...
fetch
...
different
...
representations
...
of
...
a
...
any
...
resource.
...
For
...
example
...
you
...
could
...
get
...
a
...
"default"
...
patient
...
representation
...
(only
...
including
...
the
...
patient's
...
preferred
...
name,
...
not
...
including
...
audit
...
info)
...
or
...
a
...
"full"
...
representation
...
(including
...
all
...
their
...
names,
...
all
...
audit
...
info,
...
etc).
...
Both
...
of
...
these
...
representations
...
would
...
live
...
at
...
the
...
same
...
URI.
...
If
...
the
...
client
...
wants
...
something
...
other
...
than
...
the
...
default
...
representation,
...
they
...
would
...
communicate
...
that
...
by
...
doing
...
a
...
GET
...
with
...
"Accept-Type:
...
full".
...
Is
...
this
...
appropriate?
...
If
...
so,
...
what's
...
the
...
right
...
terminology
...
for
...
this?
...
If
...
not,
...
what
...
alternate
...
approach
...
should
...
we
...
be
...
taking?
...
We
...
also
...
introduce
...
the
...
idea
...
of
...
a
...
"Ref",
...
which
...
is
...
a
...
minimal
...
representation
...
of
...
any
...
object,
...
just
...
containing
...
its
...
uuid,
...
a
...
displayable
...
string,
...
and
...
a
...
URI
...
link
...
to
...
the
...
full
...
resource.
...
So
...
for
...
example
...
if
...
I
...
fetch
...
the
...
default
...
representation
...
of
...
a
...
patient,
...
I
...
might
...
get
...
this:
...
Code Block |
---|
{
"personAddress" : {
"uuid" : "3350d0b5-821c-4e5e-ad1d-a9bce331e118",
"display" : "1050 Wishard Blvd., RG5, Indianapolis, IN",
"uri" : "http://.../ws/rest/personaddress/3350d0b5-821c-4e5e-ad1d-a9bce331e118"
},
/* more stuff */
}
{code}
|
Whereas
...
if
...
I
...
fetch
...
the
...
full
...
representation
...
of
...
a
...
patient
...
I
...
might
...
get:
...
Code Block |
---|
{
"personAddress" : {
"uuid" : "3350d0b5-821c-4e5e-ad1d-a9bce331e118",
"address1" : "1050 Wishard Blvd.",
"address2" : "RG5",
"cityVillage" : "Indianapolis",
"stateProvince" : "IN",
"uri" : "http://.../ws/rest/personaddress/3350d0b5-821c-4e5e-ad1d-a9bce331e118"
},
/* more stuff */
}
{code}
|
A
...
more
...
relevant
...
example
...
of
...
this
...
is
...
that
...
an
...
encounter
...
might
...
contain
...
100
...
observations.
...
The
...
default
...
encounter
...
representation
...
would
...
just
...
include
...
a
...
displayable
...
"weight
...
=
...
70",
...
whereas
...
the
...
full
...
encounter
...
representation
...
would
...
include
...
full
...
details
...
about
...
the
...
"weight"
...
question
...
(e.g.
...
that
...
it's
...
unit
...
is
...
"kg",
...
its
...
range
...
is
...
0-250,
...
etc).
...
Is
...
this
...
okay,
...
or
...
is
...
this
...
some
...
sort
...
of
...
anti-pattern
...
that's
...
going
...
to
...
get
...
us
...
into
...
trouble?
...
We
...
also
...
have
...
the
...
idea
...
of
...
"sub-resources".
...
Take
...
the
...
example
...
of
...
a
...
"Program"
...
(e.g.
...
"Tuberculosis
...
Treatment
...
Program")
...
and
...
a
...
"Program
...
Enrollment"
...
(i.e.
...
a
...
patient
...
may
...
be
...
enrolled
...
in
...
a
...
program
...
from
...
date
...
X
...
to
...
date
...
Y).
...
Our plan
...
is
...
that:
...
- A
...
- patient's
...
- program
...
- enrollment
...
- has
...
- a
...
- top-level
...
- URI
...
- /ws/rest/enrollment/
...
- uuid
...
- (i.e.
...
- it's
...
- not
...
- included
...
- within
...
- the
...
- patient)
...
- To
...
- see
...
- what
...
- programs
...
- a
...
- patient
...
- is
...
- enrolled
...
- in:
...
- GET
...
- /ws/rest/patient/
...
- patient-uuid
...
- /enrollments
...
- To
...
- see
...
- what
...
- patients
...
- are
...
- enrolled
...
- in
...
- a
...
- program:
...
- GET
...
- /ws/rest/program/
...
- program-uuid
...
- /patients
...
- To
...
- enroll
...
- a
...
- patient
...
- in
...
- a
...
- program
...
- you
...
- can
...
- do either of:
- POST /ws/rest/patient/
...
- patient-uuid
...
- /enrollments
...
- //
...
- may
...
- omit
...
- the
...
- patient
...
- from
...
- the
...
- request
...
- body
...
- POST
...
- /ws/rest/program/
...
- program-uuid
...
- /patients
...
- //
...
- may
...
- omit
...
- the
...
- program
...
- from
...
- the
...
- request
...
- body
...
- POST
...
- /ws/rest/enrollment
...
- //
...
- must
...
- specify
...
- both
...
- patient
...
- and
...
- program
...
- in
...
- the
...
- request
...
- body
...
Does
...
this
...
sound
...
right?
...
Some Java Code
We've
...
tried
...
to
...
put
...
together
...
a
...
framework
...
that
...
will
...
make
...
it
...
very
...
quick
...
to
...
expose
...
each
...
of
...
our
...
existing
...
domain
...
objects
...
as
...
resources
...
in
...
a
...
standard
...
way,
...
while
...
still
...
giving
...
us
...
flexibility
...
to
...
step
...
out
...
of
...
that
...
pattern
...
if
...
need
...
be.
...
To
...
create
...
and
...
expose
...
a
...
resource
...
you
...
would
...
have
...
to
...
write
...
two
...
classes:
...
...
- PatientResource (contains one-line
...
- implementations
...
- of
...
- getByUniqueId,
...
- save,
...
- etc,
...
- and
...
- descriptions
...
- of
...
- the
...
- available
...
- representations)
...
-
...
- PatientController (contains short methods to connect Spring MVC's request handling to PatientResource)
We've tried to standardize the way we do CRUD via interfaces (Creatable, Retrievable, Updatable, Deletable, Purgeable), and via a base class that helps reflect these interfaces onto our pre-web-service domain objects: DelegatingCrudResource.