openEHR API implementation in Rust

I agree with @thomas.beale about the lack of inheritance in Rust. Java has polymorphism. Dart doesn’t so I used single inheritance with implements:

class Template extends AuthoredArchetype implements Serializable {
class AuthoredArchetype extends Archetype implements AuthoredResource, Serializable {
class Archetype implements Serializable {
abstract class AuthoredResource extends Object implements Serializable {

Archie (Java) implements this as:

public class Template extends AuthoredArchetype {
public class AuthoredArchetype extends Archetype {
public class Archetype extends AuthoredResource {
public abstract class AuthoredResource extends ArchetypeModelObject {
public abstract class ArchetypeModelObject extends OpenEHRBase implements Serializable, Cloneable {

@pintariching Can you please write how Rust would handle this (so that Thomas and I learn a bit about Rust :blush:). This example doesn’t use polymorphism.


I personally like Dart but I wonder if TypeScript would be a better option :thinking:
At least to help web developers work with openEHR without learning it (TypeScript could also be used for the backend but there are many other options on the server-side).
(Dart already has a Dart → JavaScript compiler which I used to create a JavaScript SDK for openEHR)

@pintariching I have to suggest that you generate your AM/RM classes in Rust.
There are different release versions of AM/RM and your app/server must work with many different versions (you cannot control which version another hospital is using to send data to your openEHR CDR).
openEHR uses BMM files to specify the AM/RM models. They can be used by your generator to write 100s of required classes for you :wink:

Im going to make bold, and probably unpopular statement here.

Walk away from AOM2 /ADL: (for now) or listen to @NeoEHR.

As an run-time implementer you really do not need to understand this stuff. It is for tooling designers and even if you want to get into tooling / utility writing , there are much easier starting points.

Get to understand how the REST API works, how to hook it up to an app/ querying etc. By all means build some libs/utilities in Rust. Get it working. Understand how new or updated template manifest them selves in new data structures. Understand where you might have to flatten out some inheritance to work in Rust and have a look in the underlying RM to see how/if that might be improved.

By all means dip into archetypes and templates from the perspective of design-time tooling/utility support but start with the .opt operational template format or even better the simpler Web template format. Basically these are 'compiled/ aggregated constraint definitions.

Ducks for cover!!

@ian.mcnicoll Where were you with the same advice when I popped-up here :wink:

However @pintariching idea probably includes the CDR. He/she/they used API but it looks like a CDR was meant (based on):

@pintariching Can you share more about your project so that we don’t confuse you too much?
(but listen to Ian regardless of what your project is :blush:)

1 Like

I would have to look what each class extends. If it just extends with functions then In Rust I’d maybe write it like this:

struct AuthoredRespource;
trait Archetype {}
trait AuthoredArchetype {}
impl Archetype for AuthoredResource {}
impl AuthoredArchetype for AuthoredResource {}

and so on. However if there are fields added, then I’d probably do it like:

struct AuthoredResource;
struct Archetype {
    resource: AuthoredResource,
}
struct AuthoredArchetype {
    resource: AuthoredResource
}

And so on. I’m unsure if this is the right order on what inherits what so excuse me for it :sweat_smile:

1 Like

Okay honestly I wasn’t entirely sure what I was getting myself into but now it’s becoming more clear. I want to implement a CDR like EHRBase, so mostly the API, the Query things and such. For all of this it’s clear to me that I firstly need to be able to parse a Template and be able to validate it, which brings me to all of the AOM/ADL things. However I didn’t realize I could auto generate classes like @NeoEHR mentioned which is going to be a major help in development at least until I understand how it completely under the hood.

1 Like

You could skip most of the ADL if you decide to use JSON OPT2.

There is Nedap’s Visual Studio Code extension to convert ADSL into JSON: ADL and AQL visual studio code extension

1 Like

“For all of this it’s clear to me that I firstly need to be able to parse a Template and be able to validate it, which brings me to all of the AOM/ADL things”

To store data in the CDR, no you don’t!! You just send the instance data json blob, and tell it the name of the template to validate against (already uploaded and registered). the CDR will do the validation and give you validation errors.

If you want to do client-side validation, you do want to be able to parse the template, for this (at least for staters) I would strongly suggest you look at the Web template produced by both ehrBase and Better. THis is derived from the .opt which is pure AOM-based XML but is quite hard to get your head around.

Even I can work out Web templates!! - here is a Typescript [Asciidoc generator] (GitHub - freshehr/wt2doc: Asciidoc generator for Archetype Designer web templates) based on parsing a Web template (forked from work by @bna .

That is the fastest way to get to grips with the constraints involved, and expressed partly by the underlying RM but then with further constraints at archetype-level. Lots of opportunity to add value and experiment with tooling an utilities

Once that makes sense by all means get into .opt syntax/parsing/code generation as various including @Neo4j have done.

This is really clever and powerful technology but it does have a number of concepts that challenge almost everyone at first and its’s easy to be overwhelmed at the start . I always urge people to start at the client layer and work deeper into back-end stuff, even if that is the end goal.

The 2-level modelling paradigm and the way that archetypes graft onto the RM semantically is particularly mind-bending at first.

Great questions and keep asking!!

2 Likes

I sometimes say this in lectures about openEHR.:

…and since you want to use RUST, the third rule probalby appllies to you :slight_smile:

My advice regarding storage would be to pick a database that:

  1. is good at storing and querying tree-structures,
  2. capable of storing an existing standardised canonical openEHR serialisation format, wich currently would be JSON or XML and
  3. the database should also already have a built in query language for graphs or tree-structures

There are many such DBs, our research in the ORBDA paper indicates that for example ElasticSearch and Couchbase could be interesting for large scale deployments. If you are targeting something smaller with Rust like running openEHR in a resurce constrained environment like a Raspberry Pi then other options are likely better.

When it comes to AQL you then “just” need to make a translator that transforms AQL to the DB’s built in query language. Likely using a RUST-based parser generator and the published AQL grammar files. (A long time ago I was invovled in doing this in Java with XQuery as target since we used an XML database).

Then there is the problem of eating the openEHR elephant… Although some things have changed and the REST-spec that became openEHR standard is different than in the REST+openEHR-paper the suggested architecure breakdown into smaller pieces (Figure 3) likely still would be useful and let you complete one useful Rust-based piece at a time:

You could for example make an excellent RUST-based read-only openEHR CDR (useful as backup copy, research DB or for load-splitting/sharing of read operations) It could have full AQL capabilities without you even having to first learn and make complex validation stuff.

It could be used also in read+write scenarios by piggy-backing on EHRbase or any other openEHR CDR with validation - or just call and use their input validation code until you some time in the future may have written one (which may be a challenge in RUST).

2 Likes

@ian.mcnicoll](Profile - ian.mcnicoll - openEHR) Where were you with the same advice when I popped-up here !

Yes - sorry about that!! I failed in my duty as life-guard, trying to prevent newbie divers going straight to the highest 10m board!!

4 Likes

Okay that’s a lot of replies I did not expect this :sweat_smile:. Let me get through them one at a time.

Okay I think I’ll start with web templates then and put off the .opt parsing until I get a better idea about the system.

I didn’t really put a lot of thought in the database, I’m starting with PostgreSQL as EHRBase is also using it. If I wanna go the “Rust purist” way, perhaps SurrealDB could be a good fit? It’s still in development but I think it has a lot of features that could help with implementing an openEHR compliant CDR.

Also I want to thank everyone for your quick replies, they’ve been very helpful and gave me quite a lot to think about! I think I’ll start either like @erik.sundvall recommended with a read-only CDR with full AQL capabilities, or a CDR with only web templates support or maybe both I’ll see how much time I can dedicate!

If you want to build a CDR I think database query considerations will be pretty central. You likely don’t want to reinvent a system for tree traversing queries unless it is your main interest or if it would be a PhD project of yours…

I don’t know much about SurreadlDB, but it looks like a relational database. Plain relational DBs are not a good fit for (very variable) tree/graph structures (like openEHR) in themselves. Experienced openEHR system vendors like https://www.better.care/ that I believe offer their customers very many options regarding what relational DB to use for storage usually have other tricks like an inverted index (like Lucene) for the AQL path traversal/resolution and use the relational DB maily as a blob/JSON/XML storage plus some predifined index fields.

I believe the reason EHRbase gets away with using what looks like a relational DB is that they have focused on using PostgresQL that has a lot of nice JSON (tree) traversing functions built in, right @birger.haarbrandt and @christian? Look for things like JSONB and JsonPath in 3.5. Technical Documentation — EHRbase documentation (or the source code) for clues. (But just cloning the EHRbase approach in another language might not be the most fun and productive use of time.)

So if using a relational DB you would in the “Figure 3” approach above likely need to add an inverted index adjacent to or as part of the “EHR database” block. Or use tree-relational tricks like Dewey encoding as described in Querying Archetype-Based Electronic Health Records Using Hadoop and Dewey Encoding of openEHR Models - PubMed

Anyway if you in Rust make what in the figure is labelled as “R/W interfaces” and “Query executors” you can experiment with different storage implementations without having to redesign the rest of your application stack.

Please note that there is nothing in the official openEHR REST specs corresponding to the “Versioned Object” box of Figure 3 - since when makng the standard (some years after the openEHR-REST-paper publication) in the SEC we chose to split that into more specialised resources like “/ehr/{ehr_id}/composition” and some of it’s siblings. The “Query” box corresponds fairly well to the “/query” API plus some query storage from the “/definition/query” API.

1 Like

Back to your earlier questions:

  • OAS generated code is now at specifications-ITS-REST/codegen/oas-ehr at feature/codegen_testing · openEHR/specifications-ITS-REST · GitHub; this was an experiment around best way of describing OAS model schema so that we get best outcome when we generate code in various languages; in earlier stages I also added Rust, but looks like I did not included in the end that branch anymore. If you are interested in helping in reviewing that code, then I can then keep you updated next time I’m working on it. Or you could also try to generate done using some very dev-mode tooling I made in that branch.
  • about plans on v2: we will work it year on it, and it is not yet decided how many of the outstanding issues will be in v2 and how many in v1 as release-1.1.0; this subject is still under discussion. In any case, v2 will include a few small breaking changes as we try to fix some inconsistencies in v1, but the goal is anyway to minimize them. More links about plans: CRs - Change Requests, Open Issues and this work-overview page.
2 Likes

I think we missed that - yes they are documented but slightly confusingly perhaps, in the Intro

A quick note about SurrealDB and why I’m thinking about it is, that it supports almost everything you can think of in a database. I still need to see if it’s a good fit for this use case by trying it out! :smiley:

So the “R/W interfaces” and “Query executors” are meant like an interface between your database and your REST API, that’s how I imagine it?

2 Likes

I’ll try and play around with the tooling and check out the repo and see if I can make something. Also yes, sure I can help reviewing it :smile:

Okay if V2 is going to be similar to V1 then I don’t think it’s going to be too hard to move to it!

Yeah no I’m just dumb and missed that :sweat_smile:

I missed it too when trying to explain it to a colleague!! Knew it wassomwhere , justcouldn;lt find it. @sebastian.iancu - I think we need to somehow connect the generic Header infomation with each of the resources that might need it, and add headers to the examples. Happy to help - does this need a CR?

That’s in a way one of the reason we moved to OpenAPI format, so that we can better specify examples, and formally indicate supported headers for each path/resource. These changes will come (to some extent) with the next release, and yes, it would be helpful to have a CR.

2 Likes

Yes, EHRbase is using a hybrid approach with larger parts represented in tables, complemented by JSONb. However, this is currently being re-evaluated and we will change towards a different model that will make EHRbase more independent of Postgres and comes closer to ANSI/ISO SQL specifications.

2 Likes

It depends on what you call back and front :slight_smile:

My approach would be middle to back, then front

What’s middle? First design the architecture needed to retrieve data from an abstract storage, figure out what metadata is needed in your DB to get that data out, then design your DB. Then figure out how to get your data in, and finally design the internal management of versions. All design first. And I would also follow that order for the implementation. Then the last thing is to put the API over your platform (data first - API last approach, with strong emphasis on the architecture design).

Note: if you try to implement AQL, that will affect the structure of your database, since you need some kind of document storage (for AQL you need the trees in your DB or have a hybrid DB like relational + document).

It’s not easy, I have the scars to prove it! hehe

1 Like