Using Bahmni Forms in OpenMRS Microfrontends

We want to make it possible to use Bahmni Forms in OpenMRS 3.0 using Microfrontends. For this we don’t want to reinvent the wheel, but rather use as much as possible from the existing implementation.

Right now Bahmni Forms is mostly delivered to the frontend using the implementer-interface package. This package depends on bahmni-form-controls, which is the library delivering most of the components.

The architecture thus looks roughly like:

current

The bahmni-form-controls implicitly enforces a number of anti-patterns:

  • It performs HTTP calls directly rather than leaving that to the consuming application (i.e., these calls cannot be configured or left to a global handler)
  • It comes with global styles to be applied rather than allowing the consuming application to override or set these
  • It is delivered in a pre-bundled state, which makes it inefficient for use in arbitrary applications. While some dependencies are rather small (classnames), others are significant (lodash, immutable).
  • It delivers some components, but misses others, which are crucial (e.g., all the drag and drop handling), effectively resulting in just barely over half of the required components.
  • Puts a significant amount of logic in view-driven components instead of distinguishing between controller components and view components.

We already had a call with @binduak @angshuonline @buvaneswariarun @snehabagri thanks to the help of @mksd - there we introduced the topic and the possible options.

There are three distinct ways of introducing the implementer-interface to microfrontends. Variations of the three ways are possible, too:

  1. Fork and pick (Re-assemble bahmni-form-controls and implementer-interface components / code in a new repository)

  2. Use a generalized output from implementer-interface as actual basis; for the actual implementer-interface standalone application and the microfrontend version

  3. Build the microfrontend version from the same repository

All have their pros and cons. While (1) has the most effort, (3) has the greatest technical challenges.

We would favor a variation of (2) where all controls would be placed in bahmni-form-controls and implementer-interface would still just consume these controls from the library. It would thus reduce the creation of a new Bahmni Forms app to wiring together the components exported by bahmni-form-controls.

To be really flexible the styling and some other inner workings would need some adjustments, too.

In total, the previously shown architecture diagram would change to look as follows:

proposed

When implementing this architecture the following things would require change:

  • bahmni-form-controls should be delivered in a library state, i.e., not pre-bundled

  • bahmni-form-controls should not require global CSS to be applied and comes with isolated / changeable styling

  • implementer-interface shifts most of its components to bahmni-form-controls, except application dependent components such as the specific Router

  • All components in bahmni-form-controls are configurable with respect to their used API functions / calls, i.e., if a component depends on an API call this has to be resolved via a given prop (or alternatively via a context required by bahmni-form-controls)

Consequently, the implementer-interface would change slightly internally, however, remain as-is from the outside. In this setup there are multiple locations for new applications such as the microfrontend app:

  1. Within the same repository (implementer-interface becomes a monorepo for all apps)

  2. Within new repositories (but using the same GitHub organization)

  3. Within new repositories (organization independent)

Right now there is no recommendation for the location of these applications. For the microfrontend app specifically we would recommend to choose a location that suits the responsible/maintaining team.

As a result, the architecture for supporting the standalone application together with a new microfrontend app would look as follows:

A discussion resulting in next steps and further insights would be greatly appreciated!

Cc @angshuonline @binduak @snehabagri @buvaneswariarun @kirity @jdick @bistenes @burke

3 Likes

Thanks for initiative @florianrappl. However, in the summary above, I think there are few things I would like to correct

  1. Consumer of Bahmni Forms2.0 is not the “implementer-interface”. Implementer-interface is a tool to create “form definitions” that can be rendered from anywhere. Once you publish a form from the “implementer-interface”, it creates a “form definition” that is consumed from Bahmni’s clinical interface.
  • I would suggest checking out the form rendering and usage from Bahmni’s clinical interface . Login to Bahmni, create and publish a form, and go to patient consultation and try capturing information through the observation form.
  1. Implementer interface, as the name suggests, is an interface for implementers and used for designing and developing a form. The tooling itself need not be shipped/packaged/used/accessed from Bahmni. But the forms (form-definitions) themselves can be packaged and shipped with Bahmni. Forms are not tied to specific Bahmni implementations and forms a vital strategic of creating distributable packs of forms, which can be imported to any Bahmni implementation (easily done if you have implementer-interface’s form buildder)
  2. Implementer interface is a broader implementer’s workspace/tool and is not limited to forms. In future, there would dashboard/report/configuration management apps in there. The form designer interface is usually referred as “Form-builder”.
  3. Bahmni form controls is a library that is available and released on npmjs. This is the library that is bundled with “Implementer-interface”.
  4. The runtime code (that requires embedding in any app) is very small. As it stands “Bahmniapps”, invokes the formRenderer passing it the form definition and the data.
  5. Form controls do not make HTTP calls, and are reliant on data given (check getData, setData). Although if someone would want to make a call, I wouldn’t stop it, other than thinking of exposing a common API to invoke such calls. Like you have the fetch on MF.
  6. I think you are getting mixed up with the Designer controls - there are 2 sets of controls - for each form control (used runtime), there are corresponding “Designer controls” - that are used to render a specific control’s design time behavior. (e.g. control specific properties, events etc etc). Each such control has descriptor. You can perfectly code a control and not have a designer control associated with it, and yet use it in the form definition and get it to render and capture data.

I hope these help get you started in the right direction. Happy to get into a call for more explanation.

@snehabagri @bharatak @binduak @mddubey @akhilmalhotra1 @krishnanspace @shivarachakonda @sumanmaity112 @shashikanth

1 Like