Micro Front Ends
Micro Front Ends
Micro Front Ends
Alternatives
One possible solution is using the good old Iframe, which offers many advantages as far as
encapsulation and independence but is an old technology and suffers from a significant
scale issues. Other than Iframes, the term Web component has also been floating around
for a while.
Web components are a solution where you can create custom DOM elements that can run
independently and provide separation and css encapsulation, while this sounds like the
right direction, Web components are far from an actual solution. They are more of a concept
and the features behind it, such as shadow DOM still lack full browser support.
Our solution
At Outbrain we have been facing the issues that most veteran SPAs are facing, we have a
huge FE application, with a large team to manage it, and it’s getting rough. Seeing that
there are no outstanding solutions for MFE in the wild we decided to try to find a solution
of our own, one that is quick to implement on our current echo system. We defined several
key points that we saw as necessary for any solution to apply as a MFE .
Standalone mode
Each micro app should be capable of running completely in standalone mode, so each team
in charge of a given app should be able to run it independently from other applications.
This means each application should be hosted on a separate codebase and be able to run
locally on a developer’s computer, and in dev and tests environment.
Deployment
It should be possible to deploy each service independently to any environment including
production in order to allow freedom for the owner team to work without interfering with
other teams, If a bug fix needs to be deployed to production on the weekend no other team
should have to be involved.
Testing
Running tests independently on each micro app, that way a bug in one app is easily
identified and doesn’t reflect on other apps. That said It is necessary to have some
integration tests to check the interfaces between apps and make sure they are not broken,
these tests should be monitored by all teams.
One to many
We want to be able to use each micro app multiple times, a micro app should not care
where it runs, only be aware of its input and outputs.
Communication
There needs to be a decoupled way for the apps to communicate with each other without
actually knowing about each other, only via predefined interfaces and API’s.
Backwards compatibility
Since we are not going to rewrite our huge code base, we need something we can plugin
to our current system and gradually separate parts that can be managed by other teams.
Web Components
Finally, Any solution we go for should be aligned as much as possible with the web
components concept, even though it is currently just that, a concept, it seems that that is
the way the future is heading, and if any solution pop up in the future, aligning ourselves
with this will help us adopt future solution.
Enter Angular Lazy Loading Feature Modules
Angular has a built in concept of modules, which are basically declaration objects that
specify all the components, directives, services and other modules that are encapsulated in
a module.
@NgModule({
imports: [CommonModule],
declarations: [ WelcomeComponent],
bootstrap: [],
entryComponents: []
})export class AppB_Module {}
By specifying the module file as a Webpack entry point, this provided us with the ability to
bundle up the entire Angular module, including css, and html as a single standalone js file.
entry: {
'appB_module': './app/appB.prod.module.ts'
}
Using Angular lazy loading mechanism, we can dynamically load this js file and bootstrap
in into our current application.
const routes: Routes = [
{
path: appB,
loadChildren: '/appB/appB_Module#AppB_Module'
}
]
This is a big step towards our goal of separating our application to a mini application.
DOM encapsulation
In order to tackle css encapsulation we wrapped each mini app with a generic angular
component, this component uses angular css encapsulation feature, we have two options,
we can use either emulated mode or native mode depending on the browser support we
require, either way we be sure that our css will not leak out.
@Component({
selector: 'ob-externals-wrapper',
template: require('./externals-wrapper.component.pug')(),
styleUrls: ['./externals-wrapper.component.less'],
encapsulation: ViewEncapsulation.Native
})
This wrapper component also serves as a communication layer between each mini app and
the other apps. all communication is done via an event bus instance that is hosted by each
wrapper instance, by using an event system we have a decoupled way to communicate data
in and out, which we can easily clear when a mini application is cleared from the main
application.
If we take a look at the situation we have so far, we can see that we have a solution that is
very much inline with the web component concept, each mini application is wrapped by a
standalone component, that encapsulates all js html and css, and all communication is done
by an event system.
Testing
Since each application can also run independently we can run test suites on each one
independently, this means each application owner knows when his changes have broken
the application and each team is concerned mostly with their own application.