Transcript
This transcript was autogenerated. To make changes, submit a PR.
Jamaica real
time feedback into the behavior of your distributed systems and observing
changes options errors in real time
allows you to not only experiment with confidence, but respond
instantly to get things working again.
Code hello
and welcome everyone to the Talk platform freedom with microfrontend.
So this talk will take you through a list of microfrontend strategies,
a new framework that was built on top of microfrontend,
and how this new framework helped in building an application
that can run on various distributed systems.
So before getting into the topic, let me introduce myself.
I'm Saravana Balaji Srinivasan. I work as a senior software
engineer at Red Hat with the role full stack developer mainly on
JavaScript technologies. I work in the areas where we build
tools for business automation products,
serverless workflow specifications, et cetera.
So yeah, that's about me getting
back to the topic. So this talk I wish to deliver
in such a way like sharing my experience and
the learnings we at Red Hat had while implementing microfrontend
strategies for one of our use case.
So to start with, maybe let me start with
the red hat story. So sometime back we at Red
Hat had a certain use case where we had to migrate
one of our legacy product called JBPM,
which is a business automation product. We had to
migrate the product that can accommodate
some of the latest technologies and it must accommodate the future technologies as
well. So that's when we were thinking of microfrontend
strategies. We had some sort of learning, sort of it,
and then we came up with a new framework on top of
microfrontend, which we named it as the multiplying architecture.
And this multiplying architecture helped in solving
some of the problems that we had. And finally it helped us
to achieve the goal that we had. So the upcoming slides will
take you through the list of learnings that we had out of the
microfin ten and the new framework. And so yeah,
that's the talk is going to be about.
So firstly, I would like to break my topic, the platform
freedom with microfin tens and so on. I would like to emphasize
what I mean by the term platform here and the list of platforms
that we were targeting for our use case. So we all
know that web technology is totally dominating the software development
at this point. So where the developer or the company
who wish to build an application for their use case,
web technology is considered as their default choice,
right? So this is because web technology has stronger
foundation, stronger standards,
patterns, techniques, architectures, et cetera.
And also web technologies are rich in ecosystem. So when
we speak about ecosystem, we cannot ignore javascript,
though it has initially started as a scripting language. Now we can find javascript
everywhere. So over the period of time it has grown
fastly. So that's the power of JavaScript at this point.
And after the introduction of typescript,
people like me who started a career as
a backend developer on Java technology started
liking typescript after learning started liking
web technology after learning typescript because
my code is totally type safe, right?
And also I could emphasize that browser is
everywhere at this point. You can find browser
on your laptop, mission, mobile servers,
et cetera, right? So now browser is just
not only a tool to access your Internet or
to access your guis, it has become more than that.
So when I say browser is everywhere,
one can run the web components anywhere,
wherever you have the browsers, right? So these are the list of platforms
that we were targeting. We wanted our
web components to run on visual studio code
as an extension and browser as an extension.
Say for example, if you have GitHub, sorry,
Chrome as a browser, my web component must run
as a chrome extension, and in case of Firefox, it must run as a Firefox
extension. And my same set of web
components must run on GitHub as well as an extension. And finally
we had web application as an option as well. So the
reason for choosing these platforms is that the
web components that we had were tooling
components. These tools are going to be used
by the developers who are going to build an application out
of the business products that we have. And these
are going to mainly used by the developers. And developers mostly use
these four platforms, right? So that's
why we were targeting these main four platforms.
So while having this use case in mind,
we were thinking about various architecture that were existing,
starting from evolutionary architecture, and then the microservice,
which is pathbreaker on the back end world
that allows breaking a bigger monolith back end
application into a smaller microservices and without
being dependent on each service, and then
code the serverless, which is a buzzing word at this point,
which is another way called as back end as a service. You don't have
to own an entire server, you can just have
your back end logics run as a function and
you can just pay for the number of hits that
your function gets. So you don't have to own an entire server.
So then comes the microfrontend, which is
something that we are going to discuss elaborately in this talk.
So I don't want to go and answer
the question, what are microfrontends?
You might have gotten enough answers for this question
in lot of talks, right? So you
might have seen images like this with a
lot, I mean tall pillars and that
represents monolith application, blah blah blah, et cetera.
So let me just conclude
with the quote by Cam Jackson.
A micro frontend is an architectural style
where independently deliverable frontend applications
are composed into a greater whole.
Simply the goal of microfrontends is
to decouple your monolith application.
So when you have a monolith chunk of application which
is tightly coupled, and you work with one
big vast team, you are
obviously bound to lot of issues,
bugs, and addressing all these bugs and issues will
be very difficult. And the downtime of an bonolith application is
also quite high. So definitely this has to be
broken down into smaller microfrontends.
So there is an idiom in English where they
say don't put all your eggs in one basket.
So definitely this applies for the microfrontend as well. You don't have
to put all your code in one bigger chunk
of monolithic application. You can diversify your application.
So diversification is just not only applicable for your money,
so it also applies for the technology as well. You can
diversify your application into smaller microfrontends
and you can make it independent. And each microfrontend
can be accessed or developed by
a separate autonomous team.
So this diagram will give you an example of
how microfrontend application looks like.
So as I mentioned earlier in my previous slide, I work for a team
where we build tools for various products. This is
one such tool which is called as DMN, which handles
business law rules, and it represents the
business rules in a graphical manner. So considering this
view, a header component can be broken down into a
separate microfrontend. You may think here the header looks
very small, why would that qualifies as
a separate microfrontend? So decoupling
a micro frontend or breaking your UI into a smaller micro frontend is definitely
depends on the organization decision as well, or the team
decision as well, whether you definitely want to break that particular
component as a micro frontend or not.
But on the other side, consider if you
have an ecommerce kind of an application where header itself may contain
a huge chunk of react components or
any framework, right? So starting with your profile section,
your company logo, search bar,
drop downs and sub drop downs, too many components,
right? So this definitely qualifies for separate microfrontend
and could be made in by a separate team. So coming back to this view
here, so as I mentioned, this header couldn't qualify as a separate microfrontend
and the editor at the bottom which represents the graph definitely
could maintain as a separate microfrontend and
could be maintained by a separate team. And there is a navigation bar at the
left could be maintained as a separate microfrontend. All these microfrontend ends
are contained inside one container application which takes
the major decisions.
So while speaking about the Maya microfin
or speaking about decoupling,
microfrontend is not the only option
that we have to decouple a monolith application. There are
other strategies or techniques as well. So one such
as modular monolith where your front
end could be broken down into smaller uis and libraries
could be shared among this, but still these uis are tightly coupled
among them contained inside one single application.
And there is another strategy which is called as integrated application,
though here the uis
could be broken down into smaller applications and the libraries
could be shared between the applications. But this follows
a build time composition and finally
we have the strategy that we are discussing which is microfrontend
where the UI applications
pages are broken down into smaller applications
and this microfrontend end follows runtime composition.
So we will see what this built time composition
or runtime composition elaborately in
the upcoming slides.
So again, this slide will give you an idea how micro
frontend looks like and how microservice looks like and how they are differs.
So the first one shows you how a monolith application looks
like. You have your front end, you have your back end, and that back end
connected to the data source. The request from the frontend passes through the
back end and that backend fetches the data from the
data source and passes on to the front end. When it comes to the microservice,
your back end is broken down into smaller microservices. And there
comes another layer between the front end and microservices
which is called as the gateway API which takes
the decision of routing the request from the frontend to the
corresponding microservice, which can handle that particular request.
After the introduction of microfrontend, the frontend applications
is broken down into smaller pieces and that communicates to the API
and that API routes your request to the microservice.
So we will see another interesting
strategy of the micro frontend which is called as BFF.
Yes, you heard me right. Actually it is not abbreviated as
best friend forever, but it is a
best friend for the micro frontend. The actual abbreviation
for BFF is backend for
frontend. So this BFF ensures seamless
user interaction between the microservices and
microservice and the micro
frontend and the backend microservice irrespective
of the platform, wherever it is running on. So if you closely look at this
diagram, each micro frontend is connected to its own
BFF, and that BFF connects to the microservice.
So there is no direct communication from the micro frontend to the
back end. So all the communication happens between via
the BFF. And this BFF is responsible of handling
the request and response. It also converts
a response from the backend and it presents it to the microfrontend
in such a way that it can understand. And if you closely look
at this diagram and there is no communication between
the microfrontend as well, all the communication between the microfrontend
ends happens only inside the container, application only,
or the way you can call it as app shell.
So while discussing about the microfrontend strategies,
yeah, you have your microfrontend ends created and you
have your teams working on each of your microfrontends.
They are independently working fine,
but all the microfrontend has to be integrated on the
container application and it has to be presented to the user, right?
So while thinking of integration strategies, there are two
main strategies that exist. One is runtime integration.
The other one is built time integration. We have seen this makes in
one of our previous slides as well. So let me start with
this runtime integration. So imagine if a team
a building a component c and they deploys
it so that component could be accessed in this URL,
the domain name thecomponentname js
because this component is deployed as bundle whenever
the user tries to access the website,
the container application takes the whole control and
it loads the component c and it will decide when this particular
component has to be rendered. So this strategy
is kind of widely used across the people or developers who
implement micro frontend, but it has its own pros and cons.
The pro is being the teams
can deploy their components at any point, they don't have
to depend on other teams. And the downside
of this application integration strategy is the setup
time or setting up. This integration
strategy is far more complicated.
So coming to the next integration strategy that we have,
that is built time integration. So in this case, again,
considering a scenario where a team a builds
a component so that component is shipped as an NPM package,
this strategy, this NPM registry comes as a boon for us to
contain all our components as a package.
So the other team who wants to use that particular component,
they can install that particular component as a dependency to
their micro front end and they can just use it
again. This looks easy to set up and understand,
but the downside of this strategy is every
time when you implement there is a change in the
package that you build, the entire application has to be
rebuilt again. Or if you want to introduce new dependency
or new package or new component, the entire application has to
be rebuilt again. So that rebuild time is again quite high.
So there were other concerns associated with the micro frontend strategies.
One such thing is styling.
Imagine if you have various micro frontend ends
when you apply one particular style for micro frontend
that could override the styles on the other micro frontend as well,
when you unify all the microfrontends on one container application.
Yes, of course this could be resolved
by using iframes, where each
microfrontend can be rendered on the browser inside an iframe.
So that this iframe totally isolates your micro frontend. So that
changes on, I mean, style changes on one micro frontend will not
affect the other one. But there are other
concerns with using of these iframes
as well, being micro,
I mean, iframes is not something new or
something. So it's a very age old approach.
And at this point, at this evolution of technology,
I feel that nobody will prefer to use iframes.
Definitely developers across would
prefer to use or to render the microfrontend and see just inside
a dev or any other tags that they wish.
So that's when after coming across
or thinking about all these concerns that we had across the microfrontend
and strategies, we were thinking of coming up with a
new architecture. So these
were the requirements that we were thinking about.
We had actually starting with the multiple
distributions. So the web components that we have
must run on various distributions, like it has to run on vs
code as an extension, GitHub as an extension,
browsers as an extension, and finally as a web app.
And this has to happen with minimal code changes.
And there should be a bridge to the future tech stacks as well.
So in case at present I
use react framework for my web components, in future if there
are some other frameworks, for instance, if I have to use Vue,
I must be able to easily change that
framework as well. So my micro frontend
strategies, Maya, must accommodate any new technology
that I wish to migrate. So that's
when we introduced a new framework called the
multiplying architecture.
So before understanding multiplying architecture, let me understand
what is software architecture?
So according to Ralph Johnson, a software architecture
is about the important stuff, whatever that is.
So what is that important stuff? So it is abstraction.
So basically we wanted to abstract our
web components and we wanted to present to the various platforms
that we have. So abstraction is being considered as
the utmost priority for our use case to
migrate or to deploy
our applications on the platforms that I demonstrated
showed you in the previous slides.
So these are the core elements,
or the core components that we built as part of the multiplying architecture,
one as being channel, envelope,
view and editor. So the channel is being considered
as the platforms that we were discussing so far. And view
are obviously the web components that are built on react
js and envelope is something that communicates,
that enables a communication between the channel and view
in order to pass data, et cetera. And editor,
the editors that we were discussing, it could be the DMN
editors that I showed in previous slide, or there were other few editors
that we were using for products.
So we wanted our editors or the
tools to run on the web like this.
So if you look at this screenshot,
the BPMN editor is wrapped inside the envelope instance,
that envelop instance is wrapped inside the channel.
And the same editor must run the same set of web components,
must run on the browser as
an extension like this, on the GitHub as an
extension like this, and finally
vs. Code as an extension like this.
So this is how the envelope
enables the community between the channel and the view.
So if you see here, there is no iframes users.
So we are rendering our components
of the micro frontend ends via div
instead of iframes. And also these envelope,
channel and editor are contained inside,
I mean the envelope view and editors are contained inside the channel.
So the advantage of envelope is the context isolation.
So my microfrontend are
totally isolated and there is a better communication
established between the channel and the view.
And this creates an autonomous team as well. And each
team could have its own release cycles. And this
entire micro, I mean, multiplying architecture are built on top of
typescript, so it is totally type safe.
So let's see, microfiber multiplying architecture in practice.
So let me take you through the code base
that I have. I'll be sharing this URL with you where
you can access these examples. So here I have a folder called examples,
which contains a list of microfrontends.
I have some basic examples, like to do
list ping pong example, and there's another called
base 64 to PNG, which converts your image
to a PNG. So these are individual
microfrontends. And these microfrontends I wanted to run on
various platforms. Here let me take the classical
example of to do list so this is the
micro, I mean to do list view is the micro frontend and I want this
micro front end to be running on the channel vs code
as an extension and it has to run inside the
web application as well. So here if you see there is a web folder
called which is again a different microfin and you
will see a federated page for the
to do list as well. So this web app containing all the examples that I
have here to do list ping pong or base 64 et cetera.
But at this point we'll just take one example and see how it
is implemented. So firstly we'll start with how this multiplying architecture is
implemented. So this multiplying architecture follows a separate, I mean
dedicated folder structure. Let me start with the dependencies what are
that you need to install. So firstly these
are the two packages that you must use.
So these are available as part of this
key tools core. So at this point here I'm
not mentioning the version here because the core
implementing of this envelope is residing on the same repository. So I have used workspace
here. But if you are trying this multiplying
architecture you may see the exact release of
the version number, the recent release of the version number that you can see on
the NPM registry. So these
two dependencies that you may need. One is envelope and
envelope bus. These two enables the communication
between the channel and the view.
And if you go inside the SLC folder
you may see three folders,
predominantly main folders, that is API embedded and
enveloped, starting with API.
So this todo list API
so this contains list of abstracted methods
that are exposed to the external world and
channel API file this file contains a list of abstracted
methods which are exposed by the channel and consumed by
the envelope. So using these methods, the communication
between the channel and the envelope happens whatever the message that
channel has to communicate to the envelope or happens inside
these methods. So here this file contains only the abstracted
method, but the definition of these methods are provided
inside the channel. We will be seeing that as well. So there is again
another file envelope API which contains an abstracted method
which are exposed by the envelope and that are consumed by the
channel. So there is
another folder called embedded. So this is considered as a starting point
that triggers the view. And if you see here
you will see a react component embedded to do list envelope.
So you will see what this embedded to do list envelope
contains. So it is actually imported from
this folder envelope which contains envelope
to do list envelope TSX again which is a react component
here, this view, I mean to do list envelope view contains
your JSX code for the to do list application which
contains all HTML content and the CSS content
and the react code, all the use callbacks,
use state, et cetera. Yeah, it is a functional component. Again, so this
view is rendered inside the envelope, and this envelope is
called inside the embedded to do list. So this embedded to
do list envelope will be called inside the
channel. So that is something we'll be seeing here. So now let's go
to the channel vs code extension.
So to implement, yeah, in our case we had to
render our web components on the
Vs code. So we were making some sort
of configurations for the vs code extension.
So if you are also trying the same, you may have to first learn
how to create an extension on the vs code. So this folder
structure, some of the files here
deals with the configuration for the Vs code extension. So starting with
this extension ts, which will initiate the
extension and some
of its dependencies as well. And if
you see this index ts here, this todo list envelope
view envelope is initiated here. This will trigger
the react components that we saw in the multiplier.
I mean micro front end, right? So if you see here
in the package JSOn,
the micro frontend is added as the dependency here. So let's
take the easier example of web
application. Maybe you will get an idea about how this really looks.
So again,
we'll show you how here the microfrontend end
is added as a dependency here, installed as a dependency here.
And I created a separate page
for to do list which calls
the embedded to do list which I showed in my
other file. So this is imported from
key tools example todo list view, which was
the microfrontend that we were discussing so far. And this page
is called inside the app TSX where I
have provided various navigations because this web app contains
their applications examples as well. So I've provided
separate navigations for each of the examples that I have here.
And all this API app is contained. I mean
this is the root of the my react application. So let me
start this application and
see.
So yeah, I'm starting this application on
the close 9000 to
load. It's coming
up now. Yeah,
so as I mentioned, so this web app contains a list of all
the examples that I have in this repository. So let me go to
this to do list view that we were discussing.
Maybe I'll just create list
conf 42 Javascript conference
2022.
So I created this note, maybe I can just mark it as complete, or maybe
I can unmark it. Also I can remove so just simple example
of todo list.
This is how it works. So the same set of micro
frontend component or react components were used on
vs code as an extension on the web app as an extension as well.
So let me jump back to the slide.
So so far we have
seen the multiplying architecture in
practice. So after implementing
all this channel view envelope or multiplying architecture
or microfrontend strategies, all these things still we were having some sort
of issues while integrating all the microfrontend
components into one container application,
these were the issues that we were facing. So the main highlighted
issue that we were facing across both the strategies are the
duplication of libraries that we're loading. Say for example,
if I have three microfrontends for my application,
and each of the microfrontend has react as a dependency
in its microfrontend.
When I integrate all the microfrontends, three instances
of react application react dependency is created.
So if you just think about it's like duplicating
of one dependency, though it is commonly
used across the microfrontend, three instances are created.
So imagine if you have more number of dependencies that are kind
of duplicately created across the
microfrontend, either the bundle size or the size of your application
will become too high.
To overcome this issue, Federated modules came
to our rescue. So you might have heard about this
term federated modules. This was introduced in the webpack in its
fifth version. So this gave us solution
to share dependencies across the microfrontends.
And this also allowed us to implement the runtime integration
as well. So here, this is how the webpack configuration
looks like implementing the modules Federation.
So initially the module Federated has to be imported from the package.
And here on the plugins you must create an instance for
the module Federation which must contain list of routes
for the number of microfrontends that
you have. And these routes will be rendered on
the browser only when it is called, the particular microfrontend
is called. And if you see here on the line number 23, the dependencies
are shared among all these microfrontends so that there
won't be duplicate instance of dependency also
on the react side of it. So this is how each microfrontend
will be rendered. On the view we
are making use of the lazy imports from the react,
we are importing the microfrontends only when it is needed.
And here we have created routes and we
are just navigating to the respective microfrontend and whenever it is called.
So finally, after implementing the microfrontend
I mean the multiplying architecture. With the microfrontend strategies, along with
the federated modules, we were able to achieve these
end results that we wanted, the multiple distributions with
the minimal code changes, and also we were able to
achieve the bridge between the tech stacks.
So these were the achievements that we may make out of the multiplying architecture.
We were able to implement the microservice architecture,
and we were able to take the advantage of runtime
integration, and we created the autonomous team. There was no duplication
of libraries between the micro front ends.
And yeah, we were able to develop
a bridge between the text tags as well.
So you can find the code base that I was showing here in this URL.
In case. If you have any questions, please feel free to shoot it out.