Micro-frontends with AWS Amplify
By: Antonio Lagrotteria
Tags: AWS, Full Stack, Angular, React, Amplify

This story is the first part of a series of tutorials where, starting from a trivial micro-frontend (MFE) application, I will dive deep in AWS Amplify, a very promising framework allowing developers (and I would say start-ups) to easily build and deploy full-stack applications managed by AWS.

Goal

This mini-series will consists of different parts:

This story, Part 1, will present how to build a React / Angular micro frontend app, so it will be a “frontend-heavy” story.

We will then deploy it to AWS by using Amplify in Part 2.

We will build multi-tenancy CRUD Payments functionality based on GraphQL and DynamoDB in Part 3.

Finally, Part 4 will show how to implement a RESTful API with Api Gateway, Aurora RDS and PostgresQL.

The overall architecture will look like this:

Overall AWS Architecture

The user flow is pretty straight-forward: once logged in, users access the application by landing to a shell app, a React-based parent micro-frontend app acting as container two child Angular-based micro-frontend apps responsible for accounts and payments domains respectively.

The parent / shell app will contain the two child apps by referencing their generated bundle in the index.html page. You may already argue on how will a React-based app happily live together with an Angular one. The answer (one of many) is Web Components, which converts your framework specific module into basic custom HTML elements universally understood. There are a number of other approaches for micro-frontend web apps such as roasted iframe, javascript libraries and special features of javascript bundlers. I chose Web Components to keep it simple for demo purpose.

Prerequisites

To follow along with this demo, make sure to have installed or have access to following latest NodeJS & NPM. For future stories, make sure you create a free AWS account (do not use root account to play around!).

Let’s start with Accounts team

Let’s put our “Accounts team” hat and create an Angular 11 app from scratch, by suing Angular 11. Install it if you do not have it already:

npm install -g @angular/cli

ng new mfe-accounts

cd mfe-accounts

ng serve

A basic Angular11 app is now created (press enter when asked to choose additional features during the prompt such as routing and stylesheet format). Lets remove some redundant html and lets launch the client at localhost:4200.

Accounts app shown on localhost

Now let’s focus on two problems:

Since we will reference the generated bundle from the parent shell app, we need to ensure that we get a single bundle for each app.

How do we compile this app into a web component?

Single bundle generation

To solve the first challenge we will use ngx-build-plus package, by typing:

ng add ngx-build-plus

Above command extends Angular CLI commands with extra functionality, modifying the angular.json file. We will generate a single bundle with below command, which we can add in our scripts section in package.json:

"scripts": {

"ng": "ng",

"start": "ng serve",

"build": "ng build --prod --single-bundle",

"test": "ng test",

"lint": "ng lint",

"e2e": "ng e2e"

}

App as Web Component

Since Angular 6, Angular Elements allow to publish Angular components as custom elements, by exposing it as a web component.

ng add @angular/elements

This exposes utilities to wrap our application in a custom element. Modify the app.module.ts file as shown below in order to simply expose AppModule as a Web Component.

Angular app module exposed as Web Components

Repeat for the Payments Team

To create a Payments micro-frontend, just repeat same steps as mentioned above but applied to payments. Make sure to change port number and call the custom element as app-payments.

ng new mfe-payments

cd mfe-payments

ng serve --port 4201

With due minor changes, app should look like:

Payments app running on localhost

Let Core Team glue everything

Now, as Core Team member, it is time to display the newly created micro-frontends under the parent application. To convince you that Angular apps are exposed as custom elements, we will create the parent app as a React one and start it at http://localhost:3000:

npx create-react-app mfe-parent

cd mfe-parent

yarn start

The quickest approach to show the newly created application within the React app is to add references to the accounts and payments main bundles. This can be achieved by the following commands:

npm install copyfiles -g

cd mfe-accounts

npm run build

copyfiles "./dist/mfe-accounts/main*js" ../mfe-parent/public

cd ..

cd mfe-payments

npm run build

copyfiles "./dist/mfe-payments/main*js" ../mfe-parent/public

By executing above commands you should end up in a folder structure like below:

Next, reference both generated main bundles inside index.html file (Zone.js is also needed):

Index HTML with micro-frontend references

Note: in case you want to avoid generating hashing for the bundle name, simply update angular.json and set outputHashing field to “none” instead of “all”.

Finally, access React App.js file, modify look and feel and add custom HTML elements defined in mfe-accounts and mfe-payments modules:

Custom HTML elements added to App.js

Once refreshed, the React app will look like the below:

Congrats! We just displayed two dummy Angular-based micro-frontend applications into a parent React application.

Summary

This story has shown a proof of concept for a micro-frontends app using both Angular and React. Implementation was based on Web Components, which represent a portable option to make different frameworks talk to each.

I am conscious that things can and will be more complex than above in real life and improvements can be done in bundling strategies and building process, just to cite a couple of examples. Nevertheless, take this as a proof of concept for people approaching to this new architectural style, in the hope to give you some motivation to try things out, evaluate pros and cons and have fun. In Part 2 we will deploy this app in AWS so that users can access it. Thank you!

References