Blogs

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 :
A 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
sortable, pageable 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/