I think there are two levels of abstraction to consider. First, there are sub-systems within OpenEMR that require the core to run on. These include the CDR, Layout Engine, Forms, Patients, Facilities, Inpatient, Optometry, etc, etc, all the typical day to day usage components. Perhaps someone writes a nifty Billing Module. All of those should be easily installable through some type of click-and-install system (Or perhaps a download and install… the point is, something with a nice UI).
The second level of abstraction is parallel systems that run in tandem with OpenEMR. These are things like cTakes, Analytics, perhaps the Websocket server. I view these systems as needing the data of OpenEMR, and maybe some of the API, but for the most part they are first-class citizens. You can run Analytics without every touching anything but the database; These parallel systems should not live within the OpenEMR base; they should run, well, in parallel.
sub-system modules shouldn’t be Dockerized as they need a fully functioning OpenEMR install to work; the parallel systems however could definitely be made into containers.
I’m already fairly deep into the process of leveraging a core framework for OpenEMR modules to use. It’s been a very slow process but we are moving towards an API that allows for easier development of modules (Think inpatient). I expect the rest of this to be completed in Q4 of this year. My guess is by around Q1 of 2018 we’ll be looking at a significantly different workflow of Request/Responses inside of OpenEMR.
Let me speak briefly on the infrastructure of the sub-system modules. We recently introduced an Event Dispatcher into the system. This will allow us to create hooks for modules to interact with core elements, as well as for modules to create their own hooks for interacting with other modules. The idea is to become completely event-driven. This offers the maximum amount of flexibility, something we need given the complex nature of an EMR.
Additionally, we also recently introduced a service container level. Instead of Module A instantiating a class from Module B, Module A asks the Service Container (Available to all modules) for Module B. The Service Container has the information needed to properly instantiate Module B and give it back to A. This offers a Dependency Injection framework, which allows us to further compartmentalize our code.
Currently I’m getting the configuration layer in place so that when the core class initializes it is able to load up all the configuration files from multiple modules.