Micro Frontend with Single-SPA

In order to build applications which utilise the scalability, flexibility, and resilience of cloud computing, the applications are nowadays normally developed with microservice architecture using containers. Microservice architecture enables our applications to be composed of small independent backend services that communicate with each other over the network.

Project GitHub Repository

The complete source code of this project can be found at https://github.com/goh-chunlin/Lunar.MicroFrontEnd.SingleSpa.

Why Micro Frontend?

In general, when applying a microservice architecture, while backend systems are split up into microservices, frontend is still often developed as a monolith. This is not a problem when our application is small and we have a strong frontend team working on its frontend. However when the application grows to a larger scale, a monolithic frontend will start to be inefficient and unmaintainable due to the following reasons.

Firstly, it is challenging to keep the frontend technologies used in a large application up-to-date. Hence, with micro frontend, we can upgrade the version of the frontend on a functional basis. It also allows developers to use different frontend technologies to different functions based on the needs.

Secondly, since the source code of the micro frontend is separated, the source code of the individual frontend component is not as much as the monolith version of it. This improves the maintainability of the frontend because smaller code is easy to understand and distribute.

Thirdly, with micro frontend, we can split the frontend development team into smaller teams so that each team only needs to focus on relevant business functions.

Introduction of single-spa

In micro frontend architecture, we need a framework to bring together muliple JavaScript micro frotnends in our application. The framework we’re going to discuss here is called the single-spa.

The reason why we choose single-spa is because it is a framework allowing the implementation of micro frontend by supporting many popular JavaScript UI frameworks such as Angular and Vue. By leveraging the single-spa framework, we are able to register micro frontends such that the micro frontends are mounted and unmounted correctly for different URLs.

In single-spa, each micro frontend needs to implement their lifecycle functions by defining the actual implementation for how to bootstrap/mount/unmount components to the DOM tree with JavaScript or a different flavour of the JavaScript framework.

In this article, single-spa will work as an orchestrator to handle the micro frontend switch so that individual micro frontend does not need to worry about the global routing.

The Orchestrator

The orchestrator is nothing but a project holding single-spa which is responsible for global routing, i.e. determining which micro frontends get loaded.

We will be loading different micro frontends into the two placeholders which consume the same custom styles.

Fortunately, there is a very convenient way for us to get started quickly, i.e. using the create-single-spa, a utility for generating starter code. This guide will cover creating the root-config and our first single-spa application.

We can install the create-single-spa tool globally with the following command.

npm install --global create-single-spa

Once it is installed, we will create our project folder containing another empty called “orchestrator”, as shown in the following screenshot.

We have now initialised our project.

We will now create the single-spa root config, which is the core of our orchestrator, with the following command.

create-single-spa

Then we will need to answer a few questions, as shown in the screenshots below in order to generate our orchestrator.

We’re generating orchestrator using the single-spa root config type.

That’s all for now for our orchestrator. We will come back to it after we have created our micro frontends.

Micro Frontends

We will again use the create-single-spa to create the micro frontends. Instead of choosing root config as the type, this time we will choose to generate the parcel instead, as shown in the following screenshot.

We will be creating Vue 3.0 micro frontends.

To have our orchestrator import the micro frontends, the micro frontend app needs to be exposed as a System.register module, as shown below on how we edit the vue.config.js file with the following configuration.

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    output: {
      libraryTarget: "system",
      filename: "js/app.js"
    }
  }
})
Here we also force the generated output file name to be app.js for import convenience in the orchestrator.

Now, we can proceed to build this app with the following command so that the app.js file can be generated.

npm run build
The app.js file is generated after we run the build script that is defined in package.json file.

We then can serve this micro frontend app with http-server for local testing later. We will be running the following command in its dist directory to specify that we’re using port 8011 for the app1 micro frontend.

http-server . --port 8011 --cors
This is what we will be seeing if we navigate to the micro frontend app now.

Link Orchestrator with Micro Frontend AppS

Now, we can return to the index.ejs file to specify the URL of our micro frontend app as shown in the screenshot below.

Next, we need to define the place where we will display our micro frontend apps in the microfrontend-layout.js, as shown in the screenshot below.

<single-spa-router>
  <main>
    <route default>
      <div style="display: grid; column-gap: 50px; grid-template-columns: 30% auto; background-color: #2196F3; padding: 10px;">
        <div style="background-color: rgba(255, 255, 255, 0.8); padding: 20px;">
          <application name="@Lunar/app1"></application>
        </div>
        <div>

        </div>
      </div>
      
    </route>
  </main>
</single-spa-router>

We can now launch our orchestrator with the following command in the orchestrator directory.

npm start
Based on the package.json file, our orchestrator will be hosted at port 9000.

Now, if we repeat what we have done for app1 for another Vue 3.0 app called app2 (which we will deploy on port 8012), we can achieve something as follows.

Finally, to have the images shown properly, we simply need to update the Content-Security-Policy to be as follows.

<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: localhost:*; img-src data:; script-src 'unsafe-inline' 'unsafe-eval' https: localhost:*; connect-src https: localhost:* ws://localhost:*; style-src 'unsafe-inline' https:; object-src 'none';">

Also, in order to make sure the orchestrator indeed loads two different micro frontends, we can edit the content of the two apps to look different, as shown below.

Design System

In a micro frontend architecture, every team builds its part of the frontend. With this drastic expansion of the frontend development work, there is a need for us to streamline the design work by having a complete set of frontend UI design standards.

In addition, in order to maintain the consistency of the look-and-feel of our application, it is important to make sure that all our relevant micro frontends are adopting the same design system which also enables developers to replicate designs quickly by utilising premade UI components.

Here in single-spa, we can host our CSS in one of the shared micro frontend app and then have it contains only the common CSS.

Both micro frontend apps are using the same design system Haneul (https://haneul-design.web.app/).

Closing

In 2016, Thoughtworks introduced the idea of micro frontend. Since then, the term micro frontend has been hyped.

However, micro frontend is not suitable for all projects, especially when the development team is small or when the project is just starting off. Micro frontend is only recommended when the backend is already on microservices and the team finds that scaling is getting more and more challenging. Hence, please plan carefully before migrating to micro frontend.

If you’d like to find out more about the single-spa framework that we are using in this article, please visit the following useful links.

Front-end Development in dotnet.sg

yeoman-bower-npm-gulp

The web development team in my office at Changi Airport is a rather small team. We have one designer, one UI/UX expert, and one front-end developer. Sometimes, when there are many projects happening at the same time, I will also work on the front-end tasks with the front-end developer.

In the dotnet.sg project, I have chance to work on front-end part too. Well, currently I am the only one who actively contribute to the dotnet.sg website anyway. =)

Screen Shot 2017-01-29 at 12.49.23 AM.png
Official website for Singapore .NET Developers Community: http://dotnet.sg

Tools

Unlike the projects I have in work, dotnet.sg project allows me to choose to work with tools that I’d like to explore and tools that helps me work more efficiently. Currently, for the front-end of dotnet.sg, I am using the following tools, i.e.

  • npm;
  • Yeoman;
  • Bower;
  • Gulp.

Getting Started

I am building the dotnet.sg website, which is an ASP .NET Core web app, on Mac with Visual Studio Code. Hence, before I work on the project, I have to download NodeJs to get npm. The npm is a package manager that helps to install tools like Yeoman, Bower, and Gulp.

After these tools are installed, I proceed to get a started template for my ASP .NET Core web app using Yeoman. Bower will then follow up immediately to install the required dependencies in the web project.

screen-shot-2017-01-28-at-9-03-10-pm
Starting a new ASP .NET Core project with Yeoman and Bower.

From Bower with bower.json…

Working on the dotnet.sg project helps me to explore more. Bower is one of the new things that I learnt in this project.

To develop a website, I normally make use of several common JS and CSS libraries, such as jQuery, jQuery UI, Bootstrap, Font Awesome, and so on. With so many libraries to manage, things could be quite messed up. This is where Bower comes to help.

Bower helps me to manage the 3rd party resources, such as Javascript libraries and frameworks, without the need to locate the script files for each resources myself.

For example, we can do a search of a library we want to use using Bower.

Screen Shot 2017-01-28 at 9.44.47 PM.png
Search the Font Awesome library in Bower.

To install the library, for example Font Awesome in this case, then with just one command, we can easily do it.

$ bower install fontawesome

The libraries will be installed in the directory as specified in the Bower Configuration file, .bowerrc. By default, the libraries will be located at the lib folder in wwwroot.

screen-shot-2017-01-28-at-10-08-44-pm
Downloaded libraries will be kept in wwwroot/lib as specified in .bowerrc.

Finally, to check the available versions of a library, simply use the following command to find out more about the library.

$ bower info fontawesome

I like Bower because checking bower.json into the source control ensures that every developer in the team has exactly the same code. On top of that, Bower also allows us to lock the libraries to a specific version. This will thus prevent some developers to download some different version of the same library from different sources themselves.

…to npm with package.json

So, now some of you may wonder, why are we using Bower when we have npm?

Currently, there are also developers supporting the act to stop using Bower and switch to npm. Libraries such as jQuery, jQuery UI, and Font Awesome, can be found on npm too. So, why do I still talk about Bower so much?

Screen Shot 2017-01-28 at 11.30.58 PM.png
Searching for packages in npm.

For ASP .NET Core project, I face a problem on referring to node_module from the View. Similar as Bower, npm will position the downloaded packages in a local folder also. The folder turns out to be node_module, which is on the same level as wwwroot folder in the project directory.

As ASP .NET Core serves the CSS, JS, and other static files from the wwwroot folder which doesn’t have node_module in it, the libraries downloaded from npm cannot be loaded. One way will be using Gulp Task but that one is too troublesome for my projects so I choose not to go that way.

Please share with me how to do it with npm in an easier way than with Bower, if you know any. Thanks!

Goodbye, Gulp

I first learnt Gulp was when Riza introduced it one year ago in .NET Developers Community Singapore meetup. He was then talking about the tooling in ASP .NET Core 1.0 projects.

Riza Talking about Gulp.png
Riza is sharing knowledge about Gulp during dotnet.sg meetup in Feb 2016.

However, about four months after the meetup, I came to a video on Channel9 announcing that the team removed Gulp from the default ASP .NET template. I’m okay with this change because using BundleMinifier to do bundling and minifying of CSS and JS now without using Gulp because using bundleconfig.json in BundleMinifier seems to be straightforward.

Screen Shot 2017-01-28 at 11.59.18 PM.png
Discussion on Channel 9 about the removal of Gulp in Jun 2016.

However, the SCSS compilation is something I don’t know how to do it without using Gulp (Please tell me if you know a better way. Thanks!).

To add back Gulp to my ASP .NET Core project, I do the following four steps.

  1. Create a package.json with only the two compulsory properties, i.e. name and version (Do this step only when package.json does not exist in the project directory);
  2. $ npm install --save-dev gulp
  3. $ npm install --save-dev gulp-sass
  4. Setup the generated gulp.js file as shown below.
var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('compile-scss', function(){
    gulp.src('wwwroot/sass/**/*.scss')
        .pipe(sass().on('error', sass.logError))
        .pipe(gulp.dest('wwwroot/css/'));
})

//Watch task
gulp.task('default', function() {
    gulp.watch('wwwroot/sass/**/*.scss', ['compile-scss']);
})

After that, I just need to execute the following command to run gulp and changes made to the .scss files in the sass directory will trigger the Gulp Task to compile the SCSS to corresponding CSS.

$ gulp

There is also a very detailed online tutorial written by Ryan Christiani, the Head Instructor and Development Lead at HackerYou, explaining each step above.

Oh ya, in case you are wondering what is the difference between –save and –save-dev in the npm commands above, I like how it is summarized on Stack Overflow by Tuong Le, as shown below.

  • –save-dev is used to save the package for development purpose. Example: unit tests, minification.
  • –save is used to save the package required for the application to run.

Conclusion

I once heard people saying that web developers were the cheap labour in software development industry because they are still having the mindset that web developers just plug-and-play modules on WordPress.

After working on the dotnet.sg project and helping out in front-end development at work, I realize that web development is not an easy plug-and-play job at all.

Picking Up SCSS

sass-web-compiler-visual-studio-pluralsight

Last week, during our work discussion, we came to this point where we argued if “fast first, slow later” or “slow first, fast later” is suitable in our working environment.

In startup mode, everything comes at you quickly, and you tend to react fast. So in the first two years of setting up the Innovation Team in Changi Airport, our software development team had been working very hard and very fast to meet the deadline. Now, our company is switching from startup mode to scale-up mode where we need to shift towards doing things right more often than doing things fast.

Hence, we are working on setting up a set of suitable development and design principles in our development team. Applying SCSS to refactor our CSS is part of this time-consuming, difficult, and tiring process.

Installing Web Debugger in VS2015

After the introduce of Web Essentials 2015, features such as compiling SCSS files have been moved to another extension called Web Compiler in Visual Studio.

Hence, to get started in VS2015, we need to first download Web Compiler via Tools > Extensions and Updates.

Installing Extensions in VS2015.png
Installed Web Compiler in Visual Studio 2015

We will need to restart Visual Studio after the installation. Once the Visual Studio is restarted, we then can start using SCSS in our web projects.

By using Web Compiler, every time we save the .scss file, it will auto compile it to be a corresponding .css file (with minified version as well!).

Another feature that I like in this extension is that Visual Studio will specify whether the SCSS files are “Compiled successfully” or there is any SCSS error, as shown in the screenshot below.

SCSS Error Reporting in VS2015.png
Visual Studio will provide friendly error messages for SCSS too!

Refactor CSS into SCSS

Previously, besides using CSS from Bootstrap, we mostly handcrafted our CSS. Recently, it had become quite hard to maintain. So I started to refactor the CSS files from one of our web projects into SCSS.

Firstly, I created a new set of blank SCSS files while keeping the existing CSS files untouched. Secondly, I change the CSS reference of the website to use the new CSS files generated by the Sass pre-compiler. By doing this, I can choose to slowly refactor the existing CSS.

Change I Love #1: Introduction of Variables

Taking just brand colour as an example, currently our CSS files have it all over the place. The same shade of blue appears a lot of times. It is incredibly hard and time consuming to make changes in our web projects using plain CSS.

Previously, for example, we have the following CSS.

.btn-main {
    background-color: #28c8f0;
    border-color: #28c8f0;
}

The primary colour #28c8f0 is used in other classes throughout the whole CSS. Hence, we can just define it as a variable $primary-color: #28c8f0; and then use it

.btn-main {
    background-color: $primary-color;
    border-color: $primary-color;
}

In the future, if we want to change the primary colour to another colour, we just need to change it at one place without worrying if we miss out any part of the CSS not updated.

Change I Love #2: DRY with Mixin

Don’t Repeat Yourself (DRY), if we are using plain CSS, we normally find ourselves reusing the same set of codes throughout the CSS files. So, by using mixins in SCSS, there will always be one and only one set we need to remember and reuse.

Before using SCSS:

.customized-width-250 {
    margin-top: 4px;
    border: 1px solid #ffffff;
    border-radius: 5px 5px 5px 5px;
    font-weight: bold;
    height: 30px;
    min-width: 250px; 
}

.customized-width-120 {
    margin-top: 4px;
    border: 1px solid #ffffff;
    border-radius: 5px 5px 5px 5px;
    font-weight: bold;
    height: 30px;
    min-width: 120px; 
}

.customized-width-60 {
    margin-top: 4px;
    border: 1px solid #ffffff;
    border-radius: 5px 5px 5px 5px;
    font-weight: bold;
    height: 30px;
    min-width: 60px; 
}

Now, by using mixin, we can easily remove the duplicates for easy maintenance.

@mixin customized-controls ($width) {
    margin-top: 4px; 
    border: 1px solid #ffffff; 
    border-radius: 5px 5px 5px 5px; 
    font-weight: bold; 
    height: 30px;
    min-width: $width;
}

.customized-width-250 {
    @include customized-controls(250px);
}

.customized-width-120 {
    @include customized-controls(120px);
}

.customized-width-60 {
    @include customized-controls(60px);
}

Change I Love #3: Loops and Conditional

On our website, we need to display representative image for each of the countries available on the portal.

If we are using plain CSS, we need to do the following for each country. For example, for Australia, we have the follows.

.country-box-australia {
    background-image: url("/images/device-country-australia.png");
}

Now we have 9 countries on our portal. So we need to repeat the lines above for 9 times. If the images are moved to another folder, then we need to update the CSS at 9 places.

In SCSS, we can use list and each loop to make the CSS more readable.

$portal-countries: australia, france, hong-kong, japan, malaysia, new-zealand, south-korea, taiwan, thailand;

@each $country in $portal-countries {
    .country-box-#{$country} {
        background-image: url('/images/device-country-#{$country}.png')
    }
}

As you see above, it also makes use of Interpolation #{} to make the code even cleaner.

Change I Love #4: Color Functions

This is helpful especially when we do the hover effect for buttons. Previously, we always needed to ask the Design Team to give us two colour codes for buttons. One for non-hover and one for hover.

So with the Color Functions in SCSS, we can now do as follows.

a {
    text-decoration: none;
    color: $primary-color;

    &:hover, &:focus {
        text-decoration: none;
        color: darken($primary-color, 20%);
    }
}

We then can have a consistent look-and-feel throughout the whole website.

Oh ya, the & character above is used to reference parent selector.

Change I Love #5: Partials

We can also have partials by starting the name of the partials with an underscore.

Because of partials, we can organize our SCSS files properly according to their functionality.

Conclusion

I believe that now given the fact that our company is already in a scale-up mode, if we keep doing everything in a hacking way, we will easily end up with technical debt soon. Having technical debt means that we will need to spend extra development work in the future because the best overall solution is not implemented in the beginning.

That is why I always welcome opportunities to learn and improve my skills. This includes learning from my teammates via our countless conversations because the conversations kept me inspired and kept me going. The team had made me a better developer. Picking up SCSS is one of the examples and it is only the beginning.

Learning Materials

Nice Report Always Comes with Colourful Charts

Normally, we will show only tabular data in our online report module. However, tables are not exciting. So, why don’t we add some charts to the report to better describe the data? Thanks to Google Charts, web developers can add colourful charts to the web pages easily.

Step 1: The Required Libraries

The first thing we need is the Google API Loader which allows us later to easily load APIs from Google. So, with the help of it, we can make use of the Google Hosted Libraries.

 type="text/javascript" src="https://www.google.com/jsapi">

After that, we will load the API which will help us drawing charts. To do so, we will have the following JavaScript code.

google.load("visualization", "1.1", { packages: ["corechart"] });

Material Design

What is Google Material Design? There is an interesting video from Google Design team sharing the ideas behind it. (Image Credit: Google Design)
What is Google Material Design? There is an interesting video from Google Design team sharing the ideas behind it. (Image Credit: Google Design)

Recently, in order to support a common look-and-feel across all Google properties and apps running on Google platforms, Material Design is introduced to Google Charts as well.

There are more than 20 of charts available in Google Chart Gallery. Depends on which chart that you would like to use, you need to load different API in order to use the Material version of each chart. I played around with some of them which are useful in my use cases.

The first chart that I use is the Column Chart which is to draw vertical bar chart. So, the API can be called as follows.

google.load("visualization", "1.1", { packages: ["bar"] });

Other than vertical bar chart, another common graphs used in report will be Line Chart. The API of Line Chart can be called as follows.

google.load("visualization", "1.1", { packages: ["line"] });

Line Chart is available on Google Charts.
Line Chart is available on Google Charts.

The third chart that I use is Timeline. This chart is different from the two charts introduced above because so far I still can’t find the non-Material version of it. So, the only way to call the API of Timeline is as follows.

google.load("visualization", "1.1", { packages: ["timeline"] });

Timeline is a chart describing the happening events over time.
Timeline is a chart describing the happening events over time.

Step 2: The Data

After we have loaded API of the chart that we want to use, then we need to pump in the data.

var data = new google.visualization.DataTable();
data.addColumn('string', 'Branch');
data.addColumn('number', 'Sales');
data.addRows([
    ['Ang Mo Kio', 1205.80],
    ['Bedok', 828.90],
    ['Clementi', 2222.10],
    ['Dhoby Ghaut', 3180.00]
]);

In the code above, I created a data table with 2 columns. Then I added 4 rows of data with addRows.

The addRows part can be done using a simple for loop. However, due to the fact that not all browsers support Tailing Comma, the for loop needs to have additional step to remove Tailing Comma.

Step 3: Chart Render

After we have the data, now we can proceed to draw the chart. For example, if we want to draw a Column Chart for the data table above, then we will use the following code.

var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));chart.draw(data);

The HTML element chart_div above is just an empty div where the chart should be rendered at.

We will then be able to get the following diagram (Non-Material).

A simple Column Chart.
A simple Column Chart.

Customization of Charts with Options

Google Charts allows us to customize the diagram. For example, we can add title for the diagram, horizontal axis, and vertical axis.

var options = {
    title: 'Sales of Branches',
    hAxis: {
        title: 'Branch'
    },
    vAxis: {
        title: 'Amount (SGD)',
        minValue: 0
    },
    legend: {
        position: 'none'
    }
 };

chart.draw(data, options);

In the code above, I not only added titles, but I also force the vertical axis to start from 0 and hide the legend by setting its position to none.

Column chart is now updated with helpful titles.
Column chart is now updated with helpful titles.

What we have seen so far is the non-Material Column Chart. So how will a Material Column Chart look like?

To get a Material Column Chart, we will change the code above to the following.

var options = {
    chart: {
        title: 'Sales of Branches',
        subtitle: '2015 First Quarter'
    },
    axes: {
        y: { 0: { label: 'Amount (SGD)' } },
        x: { 0: { label: 'Branch' } } 
    },
    legend: {
        position: 'none'
    }
};

var chart = new google.charts.Bar(document.getElementById('chart_div'));

chart.draw(data, options);

Then we will be able to get the Material version of the chart. Now, we are even able to define a subtitle for the diagram.

Yup, we successfully upgraded our chart to the Material version.
Yup, we successfully upgraded our chart to the Material version.

In case you would like convert a non-Material Column Chart to a Material version, you can do so with the code below too.

chart.draw(data, google.charts.Bar.convertOptions(options));

Challenge with Tab in Bootstrap

When I was adding Google Charts to one of my web page with tabs using Bootstrap framework, I realized there was a problem with the display of the rendered diagram. The labels in the chart are incorrectly positioned. This problem has been discussed on Stack Overflow as well.

 

Display Issues of Google Charts in Bootstrap Tabs
Display Issues of Google Charts in Bootstrap Tabs

Interestingly, if the chart happens to be in the first tab which is visible by default, then there won’t be any display issue on the chart. This problem only occurs when the charts are located in those subsequent tabs which are hidden during the first load of the page. When user clicks on any of those subsequent tabs, then the display issue will happen.

So one obvious solution is actually to only call the Google API to render the graph when the tab is clicked. To be safe enough, I actually put a delay to the click event of the tab so that the chart will be drawn 3 seconds after the the corresponding tab is clicked. This seems to help fixing the display problem.

Similar to my solution, there is also a better alternative which is to bind the draw function to the show event of the tab, as discussed on Stack Overflow. I like the solution too because of its cleanliness of the code.

Try More with Google Charts

Google Charts is undoubtedly a very easy-to-use solution for web developers to present their data. So, please try it out and be amazed by the number of charts available on Google Charts.

Summer 2015 Self-Learning Project

This article is part of my Self-Learning in this summer. To read the other topics in this project, please click here to visit the project overview page.

Summer Self-Learning Banner