How to implement micro-frontends (MFEs)
This is an experimental feature, currently available on the branch Bahmni-IPD-master of openmrs-module-bahmniapps. To follow this guide, you need to be on this branch as of now. Some details may change over time.
This guide will go step by step through implementing your own MFE with Bahmni. For example purposes, let’s say you want to create a micro-frontend named diagnostics
which will export a single component called DiagnosticsDashboard
.
Step 1: Create a MFE repository
You can clone bahmni-microfrontend-reference repository for a solid starting point. The README.md will contain a checklist for you to go through and set-up the repository with essential information.
Once you follow through the checklist, you should end up with a module name of bahmni_diagnostics
in the ModuleFederation plugin and DiagnosticsDashboard
should be part of the exposes
property of the same.
Step 2: Decide on a serving strategy for your MFE
On running yarn build
, you should get built files in dist/federation/
folder. Files within this need to be served from a web server. This would be our remote URL.
2.1: Docker container with proxy
The reference repository assumes that you want to serve your MFE from within the bahmni-docker
setup as a single container based on a reverse proxy. If you go through this route, follow the steps below
2.1.1: Update the GitHub workflow and Dockerfile to build an image named bahmni/microfrontend-diagnostics
.
2.1.2: Add appropriate entry into the docker-compose setup that you are using. This could be bahmni-docker or some fork of it as per your use case. You should end up with a container named (for example) diagnostics
. An example entry with volume mount for local development is shown below.
diagnostics:
image: bahmni/microfrontend-diagnostics:latest
profiles: ["emr", "bahmni-lite", "bahmni-mart"]
volumes:
- "${DIAGNOSTICS_PATH:?}/dist/federation/:/usr/local/apache2/htdocs/diagnostics"
2.1.3: Add a proxy entry for the container in your proxy setup. Assuming you are using bahmni-proxy, add a reverse proxy rule to resources/bahmni-proxy.conf
# diagnostics
ProxyPass /diagnostics http://diagnostics/diagnostics
ProxyPassReverse /diagnostics http://diagnostics/diagnostics
This make the apache folder accessible at /diagnostics
proxy. Confirm that https://<bahmni-host>/diagnostics/remoteEntry.js
is accessible.
Step 3: Create MFE module in openmrs-module-bahmniapps
3.1: Create source files to hold new angular module
mkdir micro-frontends/src/diagnostics
touch micro-frontends/src/diagnostics/index.js
3.2: Add WebPack entry point for new module
Completing the above steps adds a new WebPack module which will be built into ui/app/micro-frontends-dist/diagnostics.min.js
and `ui/app/micro-frontends-dist/diagnostics.min.css
.
3.3: Mock out your module for tests
Assuming that your MFE will have full test coverage in your own repository, you should mock out this module for unit tests in Bahmni to make things easier. This needs to be done in ui/test/__mocks__/micro-frontends.js
Step 4: Create a hosting React component
4.1: Add ModuleFederation config for your MFE
This sets up the remote URL accessible for dynamic imports
4.2: Create a hosting React component loading the remote component
Step 5: Register angular component
The angular module created in Step 3 would need a wrapper angular component added to, which will interface with the hosting component created in Step 4. There are two ways to achieve this
option A (recommended): Use React2AngularBridgeBuilder abstraction
We have created a good abstraction which simplifies the wrapper build process using conventions. Check it out at micro-frontends/src/utils/bridge-builder.js
option B: Manual
If you need more control over the wrapper build process, you can do it manually, following what the abstraction in micro-frontends/src/utils/bridge-builder.js
does internally.
Step 6: Use your MFE in an angular module
To use your MFE in a given angular module, you just need to include the the module as a dependency and add the required built files along with react to the html entry point. Let’s take the example of the clinical
module.
It is very important to correctly order and place the javascript and css files within the index.html. There are build marker positions in here which will make Grunt pick up minified files within and try to minify again. This will fail with the mfe build. Follow the instructions in the comments given
The last step after this is to include the module as a dependency in your init.js file.
Which then allows you to render your component in a template like this
Managing information flow
Currently, we have not set up any kind of event bus architecture for communication between host and MFE components. As a convention, we will use hostData
and hostApi
keys to do this communication.
Passing data from Host to MFE
The remote MFE can receive data from the host angular application though standard React props. As a matter of convention, it has been decided to wrap these inputs in a single prop object called hostData
. An example usage would look like
Note: Remember, angularJs maps camelCase bindings to kebab-case so the hostData
prop will be bound to host-data
binding in the html.
Host reacting to events from MFE
For now, we have decided to rely on the host app passing callbacks to the MFE through a conventional prop named hostApi
which needs to be an object. An example usage would look like
Important note regarding hostApi
For some reason, the react2angular
adapter take an extra tick to resolve the functions within hostApi
binding. Due to this, even if your propTypes mark the hostApi functions as required, React will throw reference errors for accessing functions in undefined and similar. I recommend using the optional chaining operator (?.
) to get around this
The Bahmni documentation is licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)