Micro-frontends: How to integrate them with Liferay?

Liferay & Angular

This time, I will talk to you about the integration of micro-frontends (or MFE).

On a micro-service, it is easy to expose several versions. Clients can easily call V1 or V2 of the same micro-service, he front will decide its desired version of the back.
For MFE on the other hand, we are not going to ask the Internet user to change version manually in his url by requesting V2, it would be interesting to have a way to be able to configure, move, delete, test an MFE in real time without the need to redeploy.

This is where Liferay comes in.

1 - Liferay and micro-frontends

Liferay is a tool that has been constantly evolving since the early 2000s.

Liferay is based on OSGi (micro-services principle) and is now in Java 21.
Here is an architectural diagram summarizing how Liferay works:


Over the years, Liferay has grown from a monolith to now boasting  1,390 modules  in the CE version. These modules can be easily disabled, deleted, blacklisted, overloaded, and, of course, created new ones.

Historically, Liferay managed widgets (or portlets) and always offered the ability to move, delete, and configure them in real time or ahead of time.
All this expertise has evolved in version 7.x, with the arrival of fragments :

fragment  is a reusable and hot-modifiable component  that  contains an HTML part, a CSS part, a JS part and finally a configuration part, all executed in real time, a bit like a CodePen or a Jsfiddle.

In version 7.3, these fragments have been further improved. We can now:

  • duplicate them,

  • move them,

  • export them,

  • import them,

  • compose them,

  • configure them,

  • make drafts,

  • create them live

And in version 7.4, the Client Extension (or CX) principle was added . These Client Extensions allow you to define external client extensions that you can deploy on your Liferay instance.

From now on, Liferay offers an “online” version called SaaS , even if it is still possible to do PaaS , or to host your Liferay yourself (on your PC, your server or your Kubernetes in “ Self-Hosted ” mode).
 

2 - Integrate "Objects"

As I mentioned in my previous article, Liferay offers LowCode/NoCode with Liferay “Objects” which are exposed in REST and GraphQL.

In this screen, we see that I have declared a Contract “Object” which will contain a label, a start date, an end date, etc.

And the entries of this “Object” can be viewed, created, modified, deleted in real time using widgets provided by Liferay but will also be available in the form of collections and accessible in “ headless ” as shown in the following screen accessible via the URL of the API Explorer :



Finally, for the backend, no need to worry anymore, Liferay stores the data in the database and indexes it in ElasticSearch, so no performance or limit concerns, all the data is  sortablepageable and searchable  natively.
 

3 - Integrate “Custom Elements”

Regarding the frontend part and our “MFEs”, Liferay has provided a “CustomElement” type CX that allows us to declare our “CustomElement” (or RemoteApp) and transform it into a widget that we can place wherever we want on our pages.
We can also configure it to ensure that the same MFE can behave differently depending on its configuration.
It is also possible to have one or more instances of the same MFE per page, but this requires paying close attention to the way we code to avoid interference.

To declare an MFE, you simply need to add a CX of type “Custom Element” and configure it in this way:

Here, we find what was declared in the “index.html” file of our Angular project.

We indicate the JS resources (here there are 2) and the CSS resources (here only one), then we will also indicate if we will activate the modular mode (ES Build: which will allow us to use importMaps) and if we want our MFE to be instantiable or not. As a reminder, if an element is instantiable, we can drop it several times on the same page, otherwise we can only drop it once per page.
In both cases, we can, by means of the configuration, make their behavior or display different.

4 - Integrate “ImportMaps”

Next, we have to declare our importMaps , because they are also CX, which can be added on a page, several pages (at the Master Page level) or all my pages (at the Theme level).
Knowing that Liferay already natively integrates all importMaps for React 18 (but not for Angular). It is therefore much simpler to do React in Liferay.
To declare an ImportMap, we will proceed in the same way as before:

Here, I declared an importMap that I called with the same “specifier” as the one used in my Angular 19 RemoteApp, and the url used will be the same as the one declared in the “head” of the “index.html” file.

I can also decide to download this file and host it on my Liferay (Liferay integrates a GED that natively logs files) or on my Nginx.

5 - Integrate other resources

It will also be possible to integrate other resources in the form of CX (Client eXtension). For example, static JS or CSS files, but also file overrides of the default Liferay theme (in SaaS mode, you cannot add a theme, but you can override the default theme to modify only your instance).

It will also be possible to add micro-service or batch CXs, see the Liferay page for the complete list: https://learn.liferay.com/w/dxp/liferay-development/client-extensions

6 - Integrate styles

It is also possible to create styles, Liferay allows you to create style sheets that can be applied per page or per site.

This way, I will be able to create a common design system for several pages, an entire site or several sites.

It will be possible to “brand” a site in the colors of Canon, Apple, Philips or others…

A single site can inherit CSS styles between all its pages, and I'm not just talking about colors, but also borders, rounding, fonts, spacing, titles, etc.

Above is the “Canon” version of my site and below is the “Philips” version:



And here is the configuration of the style sheet editor if I wanted to create an “Apple” site:



On the left side I can view the page of my choice and on the right side I can  modify in real time  the colors, borders, spacing, titles... until I obtain the desired effect.
 

7 - Integrate sites

Liferay also offers the ability to create page or site templates.

It is possible to create one or more site factories that will have common pages with different styles, a different favicon, a different logo and of course different data.

Each site can have different settings and each MFE can be displayed differently since it will inherit the CSS of the current site but also its settings.
For example, in our MFE, we can retrieve the data with a technical identifier configured on each of the sites.

8 - Integrate MFEs with each other

Since MFEs can be on the same page, they benefit from the same CSS styles but also the same JavaScript objects.

Liferay exposes different javascript methods like Liferay.fire(), Liferay.on(), Liferay.Util, Liferay.ThemeDisplay, Liferay.Session, Liferay.Service…

An MFE present on a Liferay page can therefore access all these methods and JS objects and interact with the other components on the same page. It can therefore retrieve the context of the current page (which user is logged in, is it in substitution mode, in which language, on which page, etc.) but also trigger a refresh of another component, send it data, listen to it, navigate to another page, etc. and this other component can be an MFE, a Liferay widget or a Liferay fragment.

It is therefore very easy to interact with the basket or the avatar to send it information or ask it to refresh and thus update its notification counter or items in the basket.

Finally, if we want to persist data, we can call a Liferay webservice, without needing to provide our connection information.

In  the animation above  , for the communication between my 2 MFEs not being aware of each other, I used the “Liferay.fire()” present natively in all Liferay pages to send a JS object:



And on the Angular 19 “RemoteApp” side, I used the “Liferay.on()”:



Then, it is possible to interact with all the elements present on the screen and this is what I did in  the animation below  between an  Angular MFE  and the  native  Liferay avatar fragment:



In this animation, we see that when I make a change to the user's profile, the web service I created and called by my MFE will add a  notification  to this same user. The MFE will therefore request a refresh of the avatar which will update its notification badge. By clicking on it, I will be able to access all my notifications to modify their status.

9 - Integrate the user lifecycle

Liferay natively takes care of user management, password policy, their roles, permissions and their session.

Liferay can also delegate to an IdP (Identity Provider) in OIDC, SAML, CAS… and can manage session replication.
In the event of a network outage, or a problem on a server, Liferay can operate in cluster mode and replicate user sessions so that there is no session loss.
And even if this case is rare, it can be useful during deployments of new versions… we can therefore ensure a 24/7 service  without interruption of service .

10 - Integrate publications

Liferay also natively offers the principle of Publications , so we will be able to experiment in advance with a new version of our pages or configurations without affecting production and schedule our publication for a date defined in advance and even be able to go back in the event of a problem.

It is also possible to define MFEs present on another domain and therefore to be able to test our localhost MFEs without needing to actually deploy them.
An extremely practical use case is to declare our local MFEs on a test environment. Thus our frontend developers will not need to install a Liferay on their computer, they will be able to run their javascript application locally and easily integrate it on the integration or testing site.
A frontend developer will be able to test his code in a given environment without impacting this environment since he will be on a dedicated site or a hidden page of a given site.

11 - Integrate the refresh of MFEs

Liferay allows you to manage the refreshing of MFEs.
Natively, when an MFE is inserted into a page, a timestamp is automatically added as a parameter and the browser will reload the resources of the displayed MFE.

Each time the “Custom Element” is deployed or modified, the timestamp will change and cause it to be automatically reloaded on all pages containing it.

If the MFE is hosted on Liferay, it will be modified and therefore reloaded every time it is deployed. But if it is hosted elsewhere (in the case of a remote app), there are two simple solutions:

  • either manually modify the CX declared in Liferay and therefore cause it to be reloaded.

  • or provide an automatic update mechanism every X minutes using for example the native Liferay “ scheduler ” mechanism which will query the deployed version of the “RemoteApp”.

12 - Conclusion

With this second article, I've covered many of Liferay's features. All of them are available in the CE version, but the DXP version provides support and four annual releases, one of which is maintained for five years. The CE version only offers one release per year, although it's possible to override a section at any time or generate a version based on the source code available on GitHub.

This tool is very easy to use, even though it offers a lot of options.

Docker images are available here for the CE version and here for the DXP version .
By default, the Docker images run in a “demo” mode, i.e. with a file database and an ElasticSearch “SideCar”. It is important to modify the default configuration in a real project.
The DXP version is included with a 30-day license allowing easy testing of this version.

If you want to set up a micro-architecture as mentioned in this article, do not hesitate to contact me or Niji for a demonstration , advice , an  installation service , training or even a turnkey project !

Depending on the volume and especially the number of simultaneous connections , it will be possible to do a very simple installation with a PostgreSQL and a Tomcat (server exposing Liferay) on a simple VM or start with a Kubernetes architecture with several replicas (in cluster mode) and an ElasticSearch.

If you've read this far, please give me a like 👍 or leave me a comment ✍️!

French version is in my sponsor's blog : https://niji.tech/