Link Ola Microfront
Link Ola Microfront
Link Ola Microfront
Masters Thesis
Faculty of Information Technology and Communication Sciences
Examiners: Terhi Kilamo
Kari Systä
May 2021
i
ABSTRACT
Lasse Linkola: Design and Implementation of Modular Frontend Architecture on existing applica-
tion
Masters Thesis
Tampere University
Information Technology
May 2021
Modular architecture in frontend applications is still a fairly fresh concept, however micro ser-
vices are widely used in backend software design. In UI development micro services are called
micro frontends. Micro frontends are adopted by big companies more and more. In this thesis,
we will go through the concepts and the implementation of a web application using micro fron-
tends. The thesis tries to find out whether a modular architecture is a good fit for large-scale web
applications.
The client had a problem where they could not easily upgrade their project dependencies. After
taking a closer look at the existing architecture, it was determined that the existing architecture had
some serious flaws that would cause issues even if the critical dependencies would be updated. It
was decided to make a comprehensive refactor for the entire web application. Due to the modular
structure of the application, micro frontends were chosen as the way to go.
After designing and implementing these changes, it was determined that micro frontends are
a suitable contender for enterprise-level applications. No major obstacles were found that would
prevent the usage of micro frontends. We found that micro frontends can bring many benefits such
as independent module deployment, team-based module development, and simplified codebases.
Keywords: micro frontend, modular architecture, single-spa, UI, React, single-page application,
SPA
The originality of this thesis has been checked using the Turnitin OriginalityCheck service.
ii
TIIVISTELMÄ
Lasse Linkola: Design and Implementation of Modular Frontend Architecture on an existing applica-
tion
Diplomityö
Tampereen yliopisto
Degree Programme
Toukokuu 2021
Avainsanat: micro frontend, modular architecture, single-spa, UI, React, single-page application,
SPA
PREFACE
Modular architecture in web applications is still a fairly new concept. In this thesis I will
introduce the concepts of a modular micro frontend architecture and the implementation.
The project was implemented in a bit less than a year. The thesis writing process took
approximately 9 months.
I decided to use this project as my masters thesis topic because it was a project that I
did as a part of my job. I found the topic interesting as micro frontends were not familiar
to me before. The project was done based on the customers need. I had the freedom to
design the architecture as I saw what would be the best. I thank my co-workers for the
help I received during the implementation.
I would like to thank my employer Enersoft Oy for the opportunity to write my masters
thesis for them. I would also like to thank the client, who allowed me to use their project
as a subject for my thesis. Finally I thank my thesis supervisor Terhi Kilamo for giving me
valuable feedback during the writing process.
Lasse Linkola
iv
CONTENTS
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Front-end Application Technologies . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1 JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.1 TypeScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.2 JavaScript Package Managers . . . . . . . . . . . . . . . . . . . . . 4
2.2 Single Page Applications (SPA) . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Compilers and Bundlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3.1 Babel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3.2 Webpack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 JavaScript Module Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Design System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6 CSS in SPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.7 Modular Design and Microservices . . . . . . . . . . . . . . . . . . . . . . . 10
2.8 Monorepo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.9 Continuous Integration, Continuous Delivery and Continuous Deployment
(CI/CDE/CD) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.10 Static Code Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Existing design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1 External modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2 Component library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4 Modular Design in Front-end development . . . . . . . . . . . . . . . . . . . . . . 17
4.1 Benefits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.1.1 Incremental upgrades . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4.1.2 Simple, decoupled codebases . . . . . . . . . . . . . . . . . . . . . 18
4.1.3 Independent deployment . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1.4 Autonomous teams . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2 Single-SPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4.3 Goals for the new design . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.4 New Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.5 Future improvements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.1 Tasks and Road map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.1.1 Future tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.1.2 New technologies used . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.2 Upgrading Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.1 Upgrading React . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
v
LIST OF FIGURES
1 INTRODUCTION
The context of this thesis is a large-scale custom ERP (Enterprise Resource Planning)
system, which handles large amounts of data traffic between different parts of the or-
ganization. The motivation of the project is to make the existing front-end system more
developer-friendly and save money in maintenance costs in the long run. The purpose of
this thesis is to design a new functional and modular architecture and map out the pro-
cess of implementation of said architecture to the existing application. The goal is to use
the latest industry-standard technologies while keeping in mind long-term maintainability.
The project at focus in this thesis has run into a problem of very outdated third-party
library versions and a messy codebase. The project is approximately 5-years-old semi-
modular UI Web application, and has seen minimal refactoring and updates on depen-
dencies over the years it has been in production and active development. It is finally
starting to hit the wall of too extensive technological debt and developing new features is
becoming increasingly difficult. The dependencies to third-party libraries and frameworks
need to be brought up to date, but the current architecture and implementation is pre-
venting the operation to be done in a sensible and sustainable way. The application has
numerous other issues such as a lack of reusable UI components and very inconvenient
management of configuration and other global variables
The goal of the thesis is to create an improved modular architecture design for the existing
application. The design will be implemented, and finally assessed, whether it achieved
the expected results. The research question of the thesis is "Are modular web applica-
tions a viable option for a large-scale projects with high rate of developer turnover?". The
key points of the assessment are maintainability, developer experience, and continuous
integration.
The thesis is divided into two parts. The first part focuses on designing an optimal archi-
tecture for a large modular front-end application and discussing the advantages achieved
by it. The second part determines the process of how the architecture can be applied
to the existing laboratory information system in such a way that it is manageable with
2
Chapter 2 has general background information about web-applications and their architec-
ture. Chapter 3 will go through the existing architecture and implementation, discuss its
flaws, and assess what needs to be changed. In Chapter 4 discusses the modular design
in front-end applications and the key technologies to achieve it. The new architecture is
described in detail. Chapter 5 maps out and goes through the steps needed to imple-
ment the modular design in the project. The chapter focuses on the technical aspects
of implementing the architecture changes on this specific project. Chapter 7 reviews the
changes that were made and assesses the end product after the redesign.
3
A frontend is the part of an application that implements the user interface. Usually, web
applications have both the frontend and the backend, where the backend is responsible
for managing the data and frontend for displaying it. There are many ways to produce
a web frontend application, but they are all built for the same platform, browser. In this
chapter, we will look at different technologies and concepts created to help to build web
applications.
2.1 JavaScript
JavaScript is a multi-paradigm programming language that implements ECMAScript spec-
ification [3]. It is best known as the language embedded in web browsers, but with the help
of NodeJS run-time, it can also be run on servers and embedded systems. Implementa-
tions of JavaScript sometimes vary slightly between different web browsers. JavaScript
is a scripting language and does not require compiling however in the web environment
it is often compiled to such a format that it is minimized and compatible with different
browsers.
When using the terms ECMAScript and JavaScript they essentially mean the same thing
[4]. In this thesis the term JavaScript will be used. According to a Stack Overflow sur-
vey, JavaScript is the most popular programming language in the world with 69,7% of
professional developers using it [5].
Since 2015, The Ecma TC39 committee has published a new ECMAScript version each
year. The versions are named by adding the year on the name e.g. ECMAScript 2015.
Currently newest ECMAScript standard is ECMAScript 2020 or in short ES2020. ES.
Next is a dynamic name that refers to whatever is the next version of ECMAScript at the
time of writing. Sometimes versions are referenced by their edition number e.g. ES2015
is the same as ES6. ECMAScript development follows a pattern of 5 different stages
from stage 0 to stage 4. Each stage represents the state of the process of adding new
features. Stage 4 means that the feature is finished but not yet included in the standard
revision.[6]
A web browser provides a client-side environment for JavaScript which defines objects
such as windows, menus, pop-ups, dialog boxes, text areas, anchors, frames, history,
cookies, and input/output. In the browser environment, JavaScript is loaded from HTML
and is reactive to user interaction, thus no main program is needed [3]. Most commonly
4
JavaScript is used to make the web page content dynamic and add functional logic which
wouldn’t otherwise be possible.
2.1.1 TypeScript
JavaScript is not a typed language, while it brings some benefits and freedoms compared
to statically typed languages, it can lead to error-prone code. To add typing features into
JavaScript, TypeScript comes into the picture. TypeScript is a superset of JavaScript, so
all the features in JavaScript are available. TypeScript can also be optional in a project,
meaning parts of the project can be done without typing and some parts with typing. This
means that TypeScript can be gradually adopted into an existing JavaScript project.[7]
Browsers do not understand TypeScript so in the context of web applications the Type-
Script code needs to be transpiled into normal JavaScript. This can be done for example
with the TypeScript CLI tool or TypeScript loader for Babel. TypeScript features are con-
figured with a tsconfig.json file.
npm (short for Node package manager) is a package manager for managing JavaScript
project dependencies [8]. NPM can also be used to refer to the npm registry, which
is a centralized package feed where most of the open-source JavaScript packages are
distributed through. npm is the piece of software that is used to maintain project de-
pendencies and npm registry is the feed where packages are located. Another popular
alternative is yarn. The core functionality is the same as npm, but it has some different
approaches on package installation and version management [9].
that is used in the application. SPA achieves faster and smoother transitions between the
views as opposed to loading an entire page every time something changes.
There is no shortage of different SPA frameworks. Most modern web applications are
SPAs in some form. The most common frameworks are React, Angular, and Vue. In this
thesis, we will mostly focus on React as it is the relevant framework in the context of the
thesis.
Virtual DOM
The virtual DOM is a concept where a virtual presentation of the UI state is kept in mem-
ory and synced with the real DOM. This process is called reconciliation which allows
React to know when something has changed and a new render is needed. React uses
the render() function to determine the new state of the UI. React then compares the pre-
vious and new state of the UI and calculates what needs to change using the virtual DOM.
To calculate the needed updates efficiently React implements a heuristic O(n) algorithm
with two assumptions:[12]
So for example for large lists keys should always be given to the list items to ensure fast
updates on the list. In most cases, this results in very fast update times.
JSX
JSX is a syntax extension to JavaScript that is used to describe the UI. It looks very
similar to HTML but it is important not to mix them up. The reason for using JSX instead of
HTML is the fact that rendering logic is inherently coupled with other UI logic, for example,
event handling, state changes, and data display preparation. Any valid JavaScript can be
embedded into JSX by using curly braces as can be seen in Program 2.1.
8 return (
9 < d i v className= " App " >
10 <header className= " App−header " >
11 <img s r c ={ l o g o } className= " App−l o g o " a l t = " l o g o " / >
12 <p>
13 E d i t <code> s r c / App . j s < / code> and save t o r e l o a d .
14 </p>
15 <a
16 className= " App−l i n k "
17 h r e f = " h t t p s : / / r e a c t j s . org "
18 t a r g e t = " _blank "
19 r e l = " noopener n o r e f e r r e r "
20 >
21 Learn React
22 </a>
23 </ header >
24 </ d i v >
25 );
26 }
27
28 ReactDOM . r e n d e r (
29 <React . S t r i c t M o d e >
30 <App / >
31 </ React . S t r i c t M o d e > ,
32 document . getElementById ( " r o o t " )
33 );
Program 2.1. An example of react application. Generated by create-react-app [1]
Hooks
Originally React components were mostly written as JavaScript classes that had certain
lifecycle functions such as componentDidMount and componentDidUpdate. Another type
of React component is a functional component which is a normal function returning some
JSX. Before introduction of hooks in React version 16.8, there was no way to have a state
or control the lifecycle of a functional components. [13]
The most important hooks React provides are useEffect and useState. The useState
hook allows a functional component to have a state. The function returns the current
value of the state and a function to change the value. The useEffect hook allows the user
to tap into the lifecycle of the component. It takes a function and a dependency array
as parameters. If the dependency array is empty, the function will be run only on the
component mount. Otherwise, the function will be run when any value in the dependency
array is changed.
7
Bundlers are tools that take in all of the source code and its dependencies and output it
in a single file. The underlying need for bundlers is dependency handling. Traditionally
JavaScript files are imported and exported through global variables and <script> tags.
In this case, the order of the <script> tags affects when and where the variables are
available. This becomes very messy very quickly and bundlers solve this problem. Other
important features bundlers offer are for example code splitting and asset management.
Code splitting allows the output to be split in smaller bundles, which can then be loaded
on-demand or in parallel reducing loading times. Asset management allows importing
files other than just JavaScript such as CSS and images.
2.3.1 Babel
Babel is the most common compiler for front-end JavaScript projects. It leverages plugins
and presets to provide a broad amount of functionality and customizability. Babel can be
configured using a file called babel.config.json. After installing NPM packages for
desired plugins and presets, they can be configured for use in the configuration file. For
example to compile React JSX syntax, @babel/preset-react preset is needed [14].
1 {
2 " presets " : [
3 [
4 " @babel / env " ,
5 {
6 " targets " : {
7 " edge " : " 17 " ,
8 " f i r e f o x " : " 60 " ,
9 " chrome " : " 67 " ,
10 " s a f a r i " : " 11.1 " ,
11 " i e " : " 11 "
12 },
13 " useBuiltIns " : " entry " ,
14 " corejs " : " 3.6.4 "
15 }
16 ],
17 " @babel / p r e s e t −r e a c t " ,
8
2.3.2 Webpack
Webpack is a static JavaScript module bundler. It does not require a configuration file
to run but it can be configured through webpack.config.js. The configuration has 6
core concepts: entry, output, loaders, plugins, and mode. Entry is the file where the
program starts and tells Webpack to start building its internal dependency graph from
there. Output defines where the final output bundle will be created and how it is named.
Loaders are used to configure tools to process other file types than JavaScript or to
use a custom JavaScript loader. Loader configuration requires a pattern of how to pick
files on which it will be applied and the name of the loader to be used. Plugins can
provide extra functionality that loaders will not provide, such as bundle optimization, asset
management, and injection of environment variables. [15] Setting mode parameter to
production, development, none enables webpack’s built-in optimizations.
9
There are many ways to create and develop design systems. Many tools have been
created to make implementing reusable UI components easy, as well as documenting
and demonstrating them. In the context of React, some of these tools are Storybook,
Atellier, and React Cosmos. More about how Storybook works in later parts of this thesis.
There are different ways to approach styling in single-page applications. One of the most
common approaches is CSS-in-JS which is a collection of ideas where the styling prob-
lems are solved by using APIs instead of convention leveraging JavaScript [18]. Some of
the benefits CSS-in-JS provides are modularity, scoping, and state-based styling. There
10
are many different implementations of CSS-in-JS and the most notable ones are libraries
called jss and styled-components.
Keywords in modular design are abstraction, information hiding, and interfaces [19]. In
computer science the objective is to abstract parts of the software through information
hiding [20]. Interfaces are one tool that can be used to hide information between modules.
Interfaces define a set of functions that the underlying system implements. Through
interfaces, different modules can communicate with each other, without having any of the
implementation details exposed.
The benefits of modules can be significant if the modules and their interfaces are de-
signed carefully. For example, modules can be developed simultaneously by different
people. The developers only have to communicate about the changes in the interfaces,
instead of the implementation specifics. Modular design makes it also easier to design
large and complicated systems. It provides a way to isolate smaller concepts into their
context and thus making it significantly easier to design good patterns on the top level of
the application. [21]
2.8 Monorepo
Monorepo (Monolithic Repositories or Multi-Package Repository) is a single repository
containing more than one project whereas the more traditional model is a single project
per repository. The monorepo model is in use in several large software companies
like Microsoft, Facebook, and Google. It has also been adopted by some open-source
projects. Monorepos can be either Monstrous monorepos or Project monorepos. Mon-
strous monorepos can be extremely large and usually contain all the organization’s code.
Project monorepos are smaller and contain a single project’s source code. Examples of
Monstrous monorepos are large organizations such as Google and Facebook and exam-
ples of project monorepos are open-source projects Babel and Ember. [23]
There are many benefits and challenges in using a monorepo. Examples of benefits
brought by monorepos are simplified dependencies, cross-project changes, and easy
11
Some of the more notable challenges that monorepos poses are code health and code-
base complexity. Monorepos do not encourage the developers to make stable and well-
defined APIs as it is easy to add dependencies between the projects. It is also more
difficult to keep track of the dependency graph, which can lead to issues in build binary
size and extra work. [23]
Monorepos have been a source of debate for a long time in many different forums. How-
ever, no clear consensus has been found on the validity of the monorepo model. Choos-
ing between a monorepo and a traditional project per repository model is difficult as both
models have their pros and cons. The decision of using monorepo should be made on a
case by case basis and is not necessarily a better approach. [23]
Continuous Delivery (CDE) is a process that ensures that the application is always in
a production-ready state by the means of automated testing and quality checks. CDE
leverages a set of practices like CI, deployment automation, and more to deliver the
software to a production-like environment. [25]
Continuous Deployment (CD) is CDE but taken a bit further. CDE automates the cre-
ation of a production-ready application, but the release needs to be done manually. CD
also automates the build process but also automates the release to the production en-
vironment. In practice, this means that when the developer pushes a code changes to
the production branch in the version control, it will be automatically processed by the CD
pipeline and released into production. Therefore there are no manual steps in CD. [25]
There are many tools available to implement a robust CI which usually implements the
same base features. CI tool is usually a piece of software running on a server, that offers
an isolated build and testing environment. Some of these tools are for example Jenkins
and TeamCity. The processes can be configured step by step to use whatever tooling is
needed by the build. There are separate tools to help to automate release processes.
Even when fully automated releasing is not desired, these tools can be very helpful in
12
managing releases. Some of the CI tools can also be used to manage deployments, but
there is also specialized software for it like Octopus Deploy.
Figure 2.1. "The relationship between continuous integration, delivery and deploy-
ment"[25]
Very often the issues fall into known categories and follow repeated patterns. To pick
out most of the common pitfalls, static checkers are employed. A static checker is a tool
that is run on the whole code base, searching for certain problematic patterns. The tool
checks for errors in the code without executing it. This process is called Static Code
Analysis. The checker is usually run after compilation and before testing. [26]
13
3 EXISTING DESIGN
The architecture is something that could be called semi-modular. It has some ostensi-
ble modularity through having the application frame and some components in a separate
project, which can be dynamically imported. Inside the projects, the code is structured in
a way that promotes their modular usage. However, this is not fully leveraged in the cur-
rent design because the way it is implemented, does not bring all the benefits of modular
design. These benefits could be for example independent development and production
delivery.
The project is divided into two applications, MainApp and Portal. MainApp contains all
the logic and presentation for all the modules. Portal is running on a separate instance
and has the common functionality between the modules, such as the application frame,
authentication, password change, etc. Portal can be accessed independently, or the
UI components can be imported from the published bundle file over the network. The
main application imports the navigation menu and authentication components from Portal
which makes it seem like there is only one instance of the navigation menu/frame, but it
is separately rendered when accessing MainApp modules.
14
The modules are independent entities of the application. They can also be viewed as
sub-applications that have their own set of functionality and purpose. Usually, only one
module is active at a time, and navigation to these modules is from the portal.
The modules are React components, implemented in such a way that they are not de-
pendent on any outside data, and are responsible for fetching all the data they need.
Module props are used to define some behavior that is dependent on the context they
are run. Such functionality could be for example getting some properties stored in the
query string. Each module stores and manages its state independently.
The state management is done using a library which was made inside the company some
years ago. The library provides an interface to create module-level state management.
This means that each module has its state object which can be accessed and modified
throughout the module. While the basic functionality is working it is not maintained and
can be difficult to use in some cases. It also has some compatibility issues with newer
React versions.
The folder structure in both projects is similar. Following the traditional JavaScript project
structure, the root path has folders src and public. The src folder includes all the source
code and assets and the public folder includes the index.html file and some static as-
sets. Inside the src the modules folder contains all the modules which can be used
independently. app folder has the top-level application which does the routing and mod-
ule composition. services implements the methods for accessing the REST data API.
lib and components folders contain reusable UI components, theme, and some utility
functions.
The configuration for the applications is stored in a JavaScript file that is located in the
public directory of the app. It means that it won’t be bundled with the rest of the app.
Instead, the configuration is injected and accessed through the window variable. The con-
figuration variable values are populated with Octopus variable substitution. Both Portal
and MainApp have their configuration files, which causes some duplication of the values.
Having the configuration stored in the window variable makes it a global variable. Global
variables are generally not recommended in programming due to the issues they tend
to cause. They can make the code hard to read as the global variable can be changed
anywhere without a restriction.
To be able to use components exported from the Portal, MainApp needs to have the same
version of React. In the scope of these projects, the version should have been updated
on both projects at the same time. Normally this would not be a major task, but what
made it difficult was that the React version upgrade for just one project was fairly big.
The update from version 15.x to 16.x introduced breaking changes that took a while to
fix. The company wanted to remove the possibility of severe version difference between
modules happening again.
Having Portal as an external module and a runtime dependency did not provide any
significant benefits, and instead made the architecture more complicated and caused
more problems.
16
Ideally, a component library would be an independent project with strict versioning. This
is not the case in the current system. The current system is difficult to maintain and
causes more issues than it solves.
17
In Web applications that are using the micro frontend architecture, the application is di-
vided into smaller and simpler chunks. These micro frontends can be maintained, devel-
oped, and delivered independently. A micro frontend could be defined as "An architectural
style where independently deliverable frontend applications are composed into a greater
whole". [21]
In a practical case, a micro frontend is a section of the UI, consisting of dozens of com-
ponents that use a UI framework such as React, Vue, or Angular for rendering the UI.
The codebase of these parts is often separated in different repositories but composed in
runtime into a single browser tab and single operating system process. One application
can have micro-frontends that use different frameworks. For example, the navigation bar
could be implemented with Vue and the page content with React.
4.1 Benefits
Micro frontends make incremental upgrades possible and new technologies can be used
in some parts of the application, without having to do it all at once. The old parts can still
be maintained as long as necessary. From a business perspective, this is also beneficial,
because the developing costs are divided in a much broader time frame, instead of one
massive rewriting project. [21]
18
Micro-frontends should not be an excuse to dismiss other clean code principles. However,
it can enforce better practices, because it causes thicker boundaries between contexts of
the application. Micro frontends enforce deliberate data and event flow between the parts
of the application.
Micro frontends usually have their independent continuous integration pipeline which
builds, tests, and deploys it to the staging environment and production [21]. Since the
deployable entities are small, there should be fewer things to think about when making a
new release. It is also easier to make smaller releases instead of waiting for other fea-
tures to finish before some other feature can be released. When the released features
are encapsulated in smaller autonomous modules, only the released modules need to be
tested. This can reduce the time it takes to execute tests in the release pipeline. However,
it is likely a good idea to still run all the integration tests to find unintended coupling with
other parts of the application.
ownership of a section of a product from the start to finish. This can make development
faster and more effective [21]. To function properly autonomous teams need to have
people with a variety of skills that cater to every stage of the development [28]. These
teams are called vertical teams, which means the team is product-oriented instead of
focusing on a single area of development. An example in frontend development would be
to have a graphic designer, programmer, test engineer, and DevOps engineer whereas a
horizontal team would have just a group of programmers focusing on the programming of
several different micro frontends.
4.2 Single-SPA
The options of tools for implementing a micro frontend application are limited. You can
either use single-spa or implement the orchestration yourself. In most cases, single-
spa is the most sensible option. Some other tools exist, but they are mostly based on
single-spa anyway. single-spa is a framework to help orchestrate and bring together
micro frontends. It helps in getting the full benefits of micro frontend architecture without
having to write the functionality yourself. These features, among others, are the ability to
use multiple frontend frameworks such as React, Vue, and Angular, independent module
deployment, and lazy loading of the modules. [2]
The framework is created, developed, and maintained by Joel Denning. The development
of single-spa was originally started at Canopy Inc. out of the desire to use React and
react-router instead of being stuck at Angular framework [2]. At the moment single-spa is
the most mature and established framework to enable micro frontend architecture.
The single-spa application architecture consists of two key parts: single-spa config and
micro frontends. Microfronteds can be then divided into 3 different types: single-spa
applications, single-spa parcels, and utility modules. Together these concepts are used
to build a complete web application. The micro frontends and the root config typically
have independent CI processes, versioning, and version control.
Single-SPA config
single-spa config often referred to as root config, is the javascript module that orches-
trates all other modules. It contains the index.html file of the application and a configura-
tion file. The index.html is the file where the whole application will be rendered to and the
configuration file is used to register the micro frontend applications.
Importmap is JSON file that tells where to find each of the micro frontends or shared
dependencies. single-spa will read this file and fetch the application code from the
configured addresses. The paths can be relative or absolute.
1 {
2 " imports " : {
3 " @org - ui / root " : "# { OrgUiRootUrl } " ,
20
Single-SPA microfrontends
An application is otherwise a normal single page app but it does not have an HTML file.
Applications take care of their internal routing and only the root path is configured in the
root config. Once the application is mounted on the DOM, it can manipulate its HTML
content freely.
Application on a single-spa page has a lifecycle. On each phase of the lifecycle, single-
spa runs a lifecycle function. The most important lifecycle functions are bootstrap, mount,
unmount, and unload. These functions excluding unload, need to be implemented by
the application. Each of these functions must return a promise which means they are
asynchronous. For most frameworks, there is a helper library to make registering an
application to single-spa easy. The lifecycle functions are implemented by the library and
minimal configuration is needed to get a functioning application. For React application
the library is called single-spa-react.
The following is an example of a single-spa react application index file. The rootComponent
refers to a React application root component, starting from where everything is like in
any other React application. The function singleSpaReact returns the lifecycle functions
which are then exported so that single-spa root config can register the application.
1 i m p o r t React from ’ r e a c t ’ ;
2 i m p o r t ReactDOM from ’ r e a c t −dom ’ ;
21
Micro frontends utility module (MFE) is a type of micro frontend that is not rendered any-
where but can export UI components or other functionality to the applications. Shared
logic between applications can be extracted to an MFE to increase code re-usage. MFEs
can be also used to share some state between the applications. For example, authenti-
cation can be implemented in a way that it has to only be initialized once in the MFE, and
other applications can just check the authentication state provided by the MFE.
22
Dependencies in JavaScript projects can easily go out of date, as there is a large number
of external dependencies and libraries that update fairly often. To keep the dependencies
up to date, it is important to make the updating process easy. In the new design, we want
to have a single location to update shared dependencies for all the modules.
Previously all the modules were behind a single version number even though the modules
are largely independent. In the future, we want to track the changes better by having the
23
Configuring URLs with absolute paths has been causing some issues when accessing
the application from different environments, for example through proxies. One of the
goals for the new architecture is to get rid of these problems and make it possible to use
relative URLs as much as possible.
The old reusable components are out of date and need to be replaced with versions
implemented using more modern React methods. The components are not versioned
and cannot be updated from a single file as the implementation has been duplicated in
multiple projects. The goal is to create a centralized and well-maintained component
library and use it as much as possible in the micro frontends.
It was decided it would be best to separate the application into several repositories, each
having its specific purpose. The separation was made mostly based on what would be
logically best, and the size of each repository was a secondary thought. Therefore some
of the repositories ended up being minimal and only have a few lines of code. The primary
goal of the new design was to separate the application frame from the primary content
of the app and centralize the configuration into one place. Another goal was to make the
modules more independent.
The central piece of the new architecture is the root config. It has a minimal amount of
code and contains only the configuration for single-spa. It routes and loads the other
modules and initializes needed polyfills on the index.html file. This part of the application
should not be touched frequently, only when new modules are added.
Originally all of the UI was in one code repository and one React project. For the moment,
we wanted to keep them in one place and not touch them too much, so the original UI
application was kept inside one single-spa module. This is the Module1 shown in the
figure 4.3. Module2 represents the future micro frontends which each contains one UI
24
One of the goals was to separate the application frame and navigation from the main
content of the app. In the figure 4.3 org-ui-portal contains the application layout and
navigation. The layout consists of a header bar on top of the page and navigation is a
menu of links to other modules. By separating the layout and navigation, we can make
sure that the UI page remains intact even if an error occurs with another module.
In the figure 4.3 org-ui-common is a utility module. As described in 4.2, it does not
have a route, but exports functionality for other modules to use. In this case, the utility
module contains Auth React component, which can be used as a wrapper to enforce
authentication for viewing a part of the application. org-ui-common also exports an API
service interface, which can be used to access backend API endpoints.
The goal of centralizing the configuration into a single repository was achieved with the
module org-ui-config. Like org-ui-common it is an utility module. The repository in-
cludes UI configuration such as API endpoints and module base routes. These can then
be accessed from other modules when needed.
The component library is a normal NPM package that implements reusable UI compo-
nents and it is not a single-spa module. In this case, the main difference between a
module and a normal library is that a module is imported in run time. Therefore if using a
module, there can be only one version in use at a time. A normal library package can be
bundled into each sub-application separately in the desired version. The downside is that
if all the micro frontends are using the same version of the library, they will be duplicated
in each bundle.
25
The new architecture at the moment consists of many different repositories. This might
make making some otherwise small changes fairly tedious to do. For example, adding a
new micro frontend would require changes in org-ui-root, org-ui-importmap, org-ui-config
and possibly org-ui-portal. This would require creating a pull request for each repos-
itory change and would have to be reviewed by someone else. It would be nice to be
able to make said changes in a single pull request and repository. This problem could be
solved by a mono repository. Repositories org-ui-common, org-ui-config, org-ui-root,
26
org-ui-importmap could be combined under a single mono repository while still preserv-
ing the ability to have individual versioning and CI process.
27
5 IMPLEMENTATION
Even though the old system shares some of the design ideas, the differences between
the old and the new implementation are quite significant. Such a large refactor requires
time and careful planning. In this chapter, we will define a plan to get the new version out
as efficiently as possible. We will also look at each step of the refactoring process and
see what needs to be done and how.
The process was divided into two segments: Tasks that needed to be done before the
first release of the new system and the improvements that can be done at a later time. It
is important to make minimum requirements for the first release version to manage the
size of the project.
The easiest point for starting was upgrading the React version as it was one of the things
that could be done before the final design for the new architecture was created. Upgrad-
ing React did not depend on other changes, but it was a dependency for many other
changes. For example, creating a new component library and using the new components
required them to be close to the same version of React.
Next up was setting up the single-spa project. At this point, We did not have a clear
understanding of how exactly single-spa works in practice. This was one of the biggest
tasks as it required some studying and initial configuration to get the project up and
running. After the base setup of the project was done, it was time to start plan out the
architecture of the big picture. The plan went through some iterations but the result was
the architecture described in the figure 4.3.
After the design was completed it was time to set up a new component library. This is
good to do as early as possible so that among other changes required by the refactoring,
the old code could be refactored to use the new components. One of the goals in long
term is to get rid of the old component library described in section 3.2 and this is a big
part of achieving that goal.
After all the foundation for the new application was built, it was time to re-implement the
navigation, which was previously done in the portalweb project described in the section
3.1. This means a complete rewrite of the navigation menu as a micro frontend. At the
same time, micro frontend utility module org-ui-common was be created to host shared
UI components such as Auth and data API interfaces.
Now it was time to finally convert the existing application modules into a micro frontend.
Each of the modules needs to be isolated to work independently. Luckily this was not a
big task since a modular code structure was already present on the old implementation.
The task includes mostly fitting the routing to work in isolation also some styling on the
app container needs to be refactored because the new app container styling is slightly
different.
Once the single-spa project is running smoothly with the old modules, it is time to publish
a new version for the development server environment. The company already has a
functioning CI system using Jenkins for automated builds and Octopus for managing
releases. Jenkins and Octopus processes need to be configured for each micro frontend.
The goal is to get as much reusable configuration as possible. For this, a Jenkinsfile
template that would need minimal changes for each micro frontend will be created.
When the single-spa project has been running for a while and all the detected issues
are fixed, it will be released to the test environment. In the test environment, the whole
application will be thoroughly tested by some of the testing staff and end-users. At this
point, we will repeat a cycle of testing and fixing found issues until it’s ready for production.
29
Some of the code in the old project is somewhat outdated and partly done by inexperi-
enced people. However as it still works well enough to be used in production, it is not
a critical thing to fix. On these parts of the application, refactoring can be done, when
new features related to said part are needed. It will add some extra development time for
otherwise small feature updates, but this way the cost of refactoring can be spread out to
a longer period.
One major thing the whole application is missing is automated testing. The company has
not had any thought or resources put into automatic testing on the frontend development.
However, this has changed recently and one of the future goals is to implement wide
automated UI testing to help with the reliability of new features and reduce the amount of
manual testing. Implementing testing is not on the scope of this project so it will be done
at a later point in time. However, it still needs to be taken to account when designing the
architecture, since it can affect the testing ability of the code.
1 c o n s t modules = ( function ( ) {
2 const reqContext =
3 require
4 . c o n t e x t ( ’ . / modules / ’ , true , / \ . module . j s x $ / ) ;
5
6 r e t u r n r e q C o n t e x t . keys ( ) . reduce ( ( r e t V a l , f i l e P a t h ) => {
7 c o n s t exportName =
8 filePath
9 . s l i c e ( 0 , f i l e P a t h . l a s t I n d e x O f ( ’ . module . j s x ’ ) )
10 . s li c e ( filePath . lastIndexOf ( ’ / ’ ) + 1);
11
12 return Object . assign ( { } , retVal , {
13 [ exportName ] : r e q C o n t e x t ( f i l e P a t h ) . d e f a u l t
14 });
15 } , {});
16 }());
17
18 module . e x p o r t s = modules ;
Program 5.1. Old module export file
This scanning implementation could not be re-implemented with the es6 syntax due to
the limitations of es6, and therefore the modules needed to be re-exported explicitly one
by one. It was determined that the cleanest way to do that was to use the export {
default as ModuleName } from "path-to-module" syntax. The statement can be used
to re-export default export as a named export.
31
Next up was refactoring the components that used some deprecated React features. One
of these was a modal component that used ReactDOM.unstable_renderSubtreeIntoContainer()
method to render outside the DOM hierarchy. This particular interface has been com-
pletely removed from React so it needed to be replaced with ReactDOM.createPortal(),
which essentially achieves the same result.
Since React developers published version 17, the developers removed the following old
react component life cycle methods:
• componentWillMount
• componentWillReceiveProps
• componentWillUpdate
In the earlier versions the functions caused a warning message, but since React 17, they
renamed these functions to
• UNSAFE_componentWillMount
• UNSAFE_componentWillReceiveProps
• UNSAFE_componentWillUpdate
These methods were widely used in the existing code base and needed to be renamed.
Luckily there was a tool provided to do it easily. I decided not to start replacing these life
cycle methods with valid ones, because it would have been too time-consuming and it
was not required for the release. It would have likely caused new bugs and testing would
have had to be even more extensive.
Some third-party libraries had breaking changes on the API. Luckily most of these changes
were not extensive, and they were easily fixed without much trouble. However, in the case
of Devexpress, I decided not to upgrade the version as it would require too much refactor-
ing. After consideration, I decided that it would be best to just make sure the old version
was working as it was before. These Devexpress components will be removed from the
code base as new features are added to the existing application.
make the JavaScript modules consumable by single-spa. The JavaScript code needs
to be traspiled and bundled into the correct format. It is highly recommended to use
a bundler such as Webpack, Rollup, or ParcelJS. In this project, Webpack was chosen
due to it being the industry standard for compiling JavaScript files into bundles. To get
Typescript features and wide browser support, Babel was chosen for transpiling.
The tool also has a prompt asking whether Typescript will be used or not. If chosen,
create-single-spa will generate Babel config for Typescript usage. In this case, Type-
script was chosen to be used. After the root config was up and running, it was time to
transform the old project into a micro frontend. The configuration was generated with the
same tool. The only difference was choosing microfrontend as a moduleType. The tool
allows us to run it in an existing project, and it will transform the configuration to com-
patible with single-spa. However, the new configuration did not work straight away as
expected. Some compilation errors needed to be fixed but the fixes were relatively simple
and minor.
1 mergeWithCustomize ( {
2 // Prevent duplicate UnusedFilesWebpackPlugin plugin
3 c u s t o m i z e A r r a y : unique (
4 " plugins " ,
5 [ " UnusedFilesWebpackPlugin " ] ,
33
47 });
Program 5.2. Root config Webpack configuration
We use function mergeWithCustomize to add our own Webpack configuration, and over-
ride the UnusedFilesWebpackPlugin. We needed to do this so we could customize the
default ignored files list. In addition we added HtmlWebpackPlugin so we could use .ejs
format for the index.html file. This allows us to inject variables to the index.html easily.
We also define some external modules with the externals property. This tells Webpack
to use these certain libraries from our shared dependencies instead of bundling them.
The script imports a JSON file that contains all the shared dependencies. The file has
the filename and the file path of the shared dependency bundle. In most cases, the
path points to a folder inside node_modules. We can control the version of the shared
dependency by editing the org-ui-shared-dependencies package.json dependencies.
Handling authorization
Auth React component is one of the components that implemented a shared logic. The
component’s job is to check if the user is authenticated and if not, redirect to the login
page. It also manages the user information in localStorage, so that it can be accessed
and updated across different browser tabs. The component is a wrapper component
and inside the wrapped application, user information can be accessed by useAuth React
hook.
To fill the need for handling data in localStorage a React hook useLocalStorage was
created to abstract the functionality away from the Auth component. Because this is a
useful feature in general, it is also exported from the shared logic utility module. The hook
takes three parameters: key: string, initialValue: string and listenToLocalStorage:
boolean, where key is the localStorage key, intialValue is the initial value of the stored
item and listenToLocalStorage is a boolean flag that determines whether the outside
changes to the localStorage value are reflected into the hook value. The hook returns
up to date value of the stored item value, setValue and clearFromLocalStorage to make
changes to the stored item. Following is an example of the hook usage:
Keeping web applications up to date with the backend APIs is often a headache. You
never know what the interface returns and you need to constantly check the API docu-
mentation to remind yourself. To solve this problem, a client-side API client was created
to access the backend. The client’s backend APIs are documented with a tool called
swagger, which automatically generates the API documentation from the code. There is
a tool called nswag, that can generate a JavaScript API client with methods to access
the backend. It also generates Typescript typings for the request parameters and return
values, which can be used in the front-end JavaScript code. We did not want to create
36
this separately for every micro frontend as many of them use the same backend API’s,
therefore it was shared through the shared logic utility module.
Configuration
The goal number one mentioned in section 4.3 was to implement a centralized configu-
ration. In short, when the product is released in different environments, we do not want
to have to configure every single micro frontend separately. To avoid this, we created a
utility module to store all the configurations needed to make the application work in differ-
ent environments. These settings include for example API access point URLs and theme
settings. The configuration module is also used to store the base routes of each micro
frontend.
One thing that was not done out of the box was building the component library in a form
that could be published with npm. As we wanted the library to work with non Type-
script projects, we needed to compile the components into regular JavaScript. On the
other, we did not want to lose the typing information for the projects that use Typescript.
Luckily this could be solved by first extracting the typings from the code using the tsc
command-line tool, and then building the project using webpack and babel combination.
The type information could be extracted with the following parameters: tsc –declaration
–emitDeclaration –declarationMap. The type extraction and building were then com-
bined into one NPM script so all the above steps could be run with a single yarn build
command. On the Webpack configuration we chose UMD as the libraryTarget to en-
sure good compatibility with different projects including single-spa.
In Storybook, you can save component use cases as stories. A story is a sandbox that
the programmer can use in development. A story is defined in a JavaScript file and in
this case each component has at least one story. Stories can also be more complicated
and describe a whole view. This is useful when demonstrating and developing the com-
ponents in cohesion.
37
5.5.1 Jenkins
The client had a Jenkins environment set up for automated building, however, the ver-
sion that was currently in use was very outdated and hard to upgrade. During the thesis
project, there was another project ongoing with the intent to create a new Jenkins envi-
ronment from scratch. The timing was perfect as this project could be configured in the
39
new environment.
Previously the Jenkins jobs were configured as normal Jenkins jobs, but the new system
leveraged the Jenkinsfile configuration. This means that every job is configured in a
file called Jenkinsfile which resides in the root of the code repository. The previous
system did not allow the configuration to be managed in version control, which made
tracking changes more difficult. The Jenkinsfile uses Groovy syntax. The CI process in
Jenkinsfile consists of stages. In our Jenkinsfile there are stages build, scan, quality gate,
pack, publish and release. Each of the stages has steps which are a sequence of shell or
Jenkins commands. For example, in the build stage commands yarn install and yarn
build are run. The stages can be run conditionally. For example, in this case, we only
wanted to run the release stage on the develop branch.
The goal was to create a Jenkinsfile that could be used with any micro frontend without
changes. The individual configuration for the job was done with environment variables. In
this particular case, customized environment variables were not needed. All the variables
are extracted from the build process environment. For example, the project name is
parsed from the branch name and the version number of the build is extracted from the
package.json file.
Jenkins can run multiple build processes concurrently, however, in our case it causes
some issues. Yarn is a tool that cannot have multiple instances running at the same time
on the same server. We ran into this issue when we noticed that sometimes builds fail due
to a cache issue. One solution for the problem would be to containerize the build process,
however, at this time the environment was not ready for it. Instead, it was decided that
the best solution for the moment was to disable concurrent builds in the Jenkinsfile.
5.5.2 SonarQube
SonarQube is a tool for static code analysis. It allows automated code checking for poten-
tially problematic patterns in the entire code base. SonarQube analysis is started from
the Jenkins pipeline. It will produce a report for every build done by the pipeline it will
either pass the quality gate or not. If the code does not meet the criteria based on the
SonarQube configuration, the build will fail.
Setting up this project for Sonarqube analysis was straightforward. For the most part, no
extra configuration was needed. Some projects had programmatically generated code,
which we did not want to be analyzed. Certain files could be excluded from the analysis by
creating a file called sonar-project.properties in the root of the project and configure
a key sonar.exclusions to point in the file or files.
40
5.5.3 Octopus
Octopus is a tool for managing and automating releases and deployments in different
environments. An Octopus project consists of the following concepts: deployment pro-
cess, channels, lifecycles, and releases. The deployment process is a sequence of steps
needed to create a release from the application package that was previously created by a
Jenkins job. A lifecycle is a set of stages/environments that the application goes through
in its lifetime. For example, the application can be first released in a staging environment
and later to the production environment. A release is the product of the deployment pro-
cess. After a release is created, it can be deployed to as many environments as needed.
Releases can be assigned to specific channels based on criteria configured in the deploy-
ment process. For example, different channels can have their lifecycles. Octopus allows
the user to do variable substitution on the package. This is very useful in configuring
variables that are dependant on the environment of the release. [29]
The client already had an Octopus environment set up and standardized application life-
cycles so none of that needed to be designed in this project. An Octopus project was
created for each micro frontend. The variable substitution was configured for the config-
uration micro frontend and the root project. This was done by using syntax #{Variable}
in the code, which then would be found by Octopus and replaced with the corresponding
variable configured in the octopus project. The variable substitution was primarily used
for configuring API endpoints and other URLs.
41
1 stage ( ’ p u b l i s h ’ ) {
2 when { branch ’ master ’ }
3 steps {
4 nodejs ( c o n f i g I d : ’ p r i v a t e −proget ’ ,
5 nodeJSInstallationName : ’ Node ’ ) {
6 sh "npm p u b l i s h "
7 }
8 }
9 }
42
6 EVALUATION
The design and implementation of modular architecture in web applications were com-
pleted successfully. During the project, plans and requirements were adjusted on multiple
occasions accordingly when issues emerged. The project did not face any major obsta-
cles even though it lasted longer than expected. This chapter reviews the original goals
and whether they were achieved in the result.
We found no great obstacles that would prevent large-scale applications from adopting
the micro frontend architecture. Based on the findings we can determine that it brings
more benefits than downsides. Most of the downsides are based on the overhead that
the architecture brings on the development so it is recommended to review each case
independently to see if the benefits of micro frontends are relevant to the application.
6.2 Goals
In section 4.3 a set of goals was listed for the transformation of the existing web applica-
tion. We will go through each of these points and see if and how they were solved in the
new design and implementation.
stored in their repositories, which means that this goal was achieved. Only when making
modifications to shared micro frontends is coordination needed. In a real scenario, most
of the time modifying shared modules means adding some values to the configuration
module. This is a simple change and does not need much of a review so most of the time
teams can work on their features independently.
Due to the nature of micro frontends and requirements by the singe-spa library, the num-
ber of needed configuration variables increased, but we did not find that it was a problem.
However, it might require a bit more time for a new developer to get familiar with the
architecture and therefore understanding all the configuration. The result is deemed sat-
isfactory, while improvements can and will still be made.
44
6.3 Concerns
The project was successful and no major concerns regarding the architecture surfaced.
However, there are always things that are possibly inconvenient and cause some headaches.
One of these things is the fact that while the architecture allows using different UI frame-
works, using the same framework with two different versions can be problematic. Of
course, the goal is not to use different versions of the same framework, but sometimes
it might be a nice temporary solution. For example, if a library update comes with a
breaking change, all the modules would not be needed to be upgraded at once. It is de-
pendant on the framework whether parallel instances work. For example, React will work
as long as the micro frontends running simultaneously do not use cross micro frontend
components.
7 CONCLUSION
The thesis was made in collaboration with Enersoft Oy. A client of Enersoft needed to
upgrade their outdated UI project and a new underlying architecture that would be more
maintainable and robust. The research question for the thesis was whether a modular
architecture in web applications is a good fit for long lifespan projects, where the turnover
of developers is high.
In the first part of the thesis, we made an introduction to single-page web applications.
We first went through the traditional single-page applications and then moved on to take
a closer look at how to utilize a modular architecture in web development. We introduced
a concept of micro frontends studied the benefits they may bring to large-scale web ap-
plications.
In the second part, we looked into an existing, rather traditional single-page application
and made a plan on how to migrate this particular application into a modular architecture.
We laid out goals we wanted to achieve in the refactoring process. We carefully planned
the steps to convert the existing architecture and implementation into a modular architec-
ture using micro frontends. The single-spa framework was chosen as the primary basis
for the application as it implements most of the features we needed to reach our goals.
The changes were implemented using the latest robust technologies while keeping long-
term maintainability in mind. After completion we determined that the micro frontend
architecture is a valid contender when choosing the architecture type of a web application
project. We also looked back on our goals and reviewed if the result accomplished those
goals. No great great obstacles were faced that would have prevented the successful
completion of the project. The goals set for the project were met in a satisfactory manner.
46
REFERENCES
[1] React - Create React App. URL: https : / / reactjs . org / docs / create - a - new -
react-app.html (visited on 09/11/2020).
[2] single-spa. URL: https://single-spa.js.org/docs/getting-started-overview
(visited on 02/10/2021).
[3] ECMAScript 2021 Language Specification. Sept. 10, 2020. URL: https://tc39.
es/ecma262/#sec-intro (visited on 09/11/2020).
[4] Wirfs-Brock, A. and Eich, B. JavaScript: The First 20 Years. Proc. ACM Program.
Lang. 4.HOPL (June 2020). DOI: 10.1145/3386327. URL: https://doi.org/10.
1145/3386327.
[5] Stack Overflow, Most Popular Technologies Survey. URL: https : / / insights .
stackoverflow.com/survey/2020/#technology- programming- scripting- and-
markup-languages (visited on 09/11/2020).
[6] The TC39 Process. URL: https : / / tc39 . es / process - document/ (visited on
09/18/2020).
[7] TypeScript. URL: https://www.typescriptlang.org/ (visited on 03/25/2021).
[8] npm - Node package manager. URL: https://www.npmjs.com/ (visited on 03/25/2021).
[9] Yarn package manager. URL: https://yarnpkg.com/ (visited on 03/25/2021).
[10] Flanagan, D. JavaScript: the definitive guide. 2006.
[11] React - A Javascript library for building user interfaces. URL: https://reactjs.
org/ (visited on 09/11/2020).
[12] React - What is virtual DOM? URL: https://reactjs.org/docs/faq-internals.
html#what-is-the-virtual-dom (visited on 09/11/2020).
[13] React Hooks. URL: https://reactjs.org/docs/hooks- intro.html (visited on
03/04/2021).
[14] Babel. URL: https://babeljs.io/docs/en/index.html (visited on 09/18/2020).
[15] Webpack - Concepts. URL: https : / / webpack . js . org / concepts/ (visited on
09/18/2020).
[16] Developer Mozilla - JavaScript Modules. URL: https://developer.mozilla.org/
en-US/docs/Web/JavaScript/Guide/Modules (visited on 09/25/2020).
[17] Godbolt, M. Frontend architecture for design systems: a modern blueprint for scal-
able and sustainable websites. " O’Reilly Media, Inc.", 2016.
[18] What actually is CSS-in-JS. URL: https : / / medium . com / dailyjs / what - is -
actually-css-in-js-f2f529a2757 (visited on 04/01/2021).
[19] Baldwin, C. Y., Clark, K. B., Clark, K. B. et al. Design rules: The power of modularity.
Vol. 1. MIT press, 2000.
[20] Colburn, T. and Shute, G. Abstraction in computer science. Minds and Machines
17.2 (2007), 169–184.
47