An approach to Micro-frontend Architecture (MVP) with NextJs
Tags: webdev, architecture, react, microfrontend
Published: 2020-06-21 18:13:36

As soon as web applications grow bigger, they become difficult to manage. So lets just follow the main idea of engineering, which is breaking the problem into smaller problems. So extending the concepts of micro-services to the frontend, we can break a huge application on the basis of routes to mini apps or MVP's (Minimum Viable Product).
<Enter>
<Enter>

![When to choose](https://dev-to-uploads.s3.amazonaws.com/i/ltljkflkkkbinmsow3k7.png)
### When should I choose
We need to choose this architecture wisely as it comes with some shortcomings as well. So, if you are working on a huge web app, and you need a quicker deliverables, which are independent to each other, the trade off would be worth it.
<Enter>
<Enter>

### Why choose Micro-frontends/MVP
This concept of route based application breakup can help out ship mini-apps faster, eliminating the risk of breaking the previously deployed MVP's.
The idea behind micro-frontends is to divide the web app to multiple features as independent products, whose ownership can be divided amongst independent teams. The idea allows each team to function across different area of business.

The idea have a lot of tech pros:
- Single Responsibility principle/ Business Centric
- Faster and Parallel shipping of products/features
- Technology Agnostic (One can run on React while other is free to choose Vue or Angular)
- Resilient - If one mini-app has a only that mini-app needs to go down or redeployed.
- Sharing of static assets

<Enter>
<Enter>

### The Repo Approach
As we are dealing with multi-apps, we need to make a choice of mono-repo or a multi-repo approach. It is not difficult to guess that handling one repo would far easier than handling multiple repos. So in case your mini-apps are using the same library/framework its suggested to take the Mono-repo approach. By this approach, the same repo would be accustomed to ship out multiple builds based on the routes you want to bind with a MVP.
However, if you want to try out multiple techs or want to separate out code you would be forced to use multi-repo.
One of the great way to manage your mono-repos is [this](https://nx.dev/react/workspace/configuration)
<Enter>
<Enter>

### Architecture Design Principles
So the design principle says that the each microfrontend should :
- [Follow the Single responsibility Principle of the SOLID rules](https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design#toc-single-responsibility-principle)
- Be Business centric
- Autonomous Feature
- Frontend Framework Agnostic
- Super Resilient


### How to Implement
Without taking more time, lets deep dive on how to break the application into micro frontends.
<Enter>
First of all, we need to categorise our mini applications. So for that, lets create a file named as miniapp.js having the below contents:
```javascript
const MINIAPPS = {
  minapp1: {
    id: 'app1',
    patterns: ['/minapp1'],
    subPath: '/mainSubpath/minapp1'
  },
  minapp2: {
    id: 'app2',
    patterns: ['/minapp2'],
    subPath: '/mainSubpath/minapp2'
  },
  minapp3: {
    id: 'app3',
    patterns: ['/minapp3'],
    subPath: '/mainSubpath/minapp3'
  },
  
};
module.exports = MINIAPPS;
```
So, to explain the above, we are planning to break our web app into 3 micro-frontends, with each having a specific subpath. So in short we are aiming to build 3 MVP's, each having a separate build, with the below paths:

www.mywebapplication.com/mainsubPath/minapp1
www.mywebapplication.com/mainsubPath/minapp2
www.mywebapplication.com/mainsubPath/minapp3

The main _mainSubPath_ is added for the extendability of multiple webapps inside _www.mywebapplication.com_. In case its not your requirement you can keep the _subPath_ property to be _'/miniapp1'_
<Enter>
So in order to read the routes from the next.config, lets create a file which has the all the routes from the application on the root of the application preferably.
*routes-list*
```javascript
const APP_ROUTES = [
  {
    name: 'Feature 1',
    page: 'miniapp1/feature1',
    pattern: '/miniapp1/feature1'
  },
  {
    name: 'Feature 2',
    page: 'miniapp1/feature2',
    pattern: '/miniapp2/my-route/feature2'
  },
  {
    name: 'Feature 3',
    page: 'miniapp2/feature3',
    pattern: '/miniapp2/feature3'
  },
  {
    name: 'Feature 4',
    page: 'miniapp2/feature4',
    pattern: '/miniapp2/my-route/my-sub-route/feature4'
  },
  {
    name: 'Feature 5',
    page: 'miniapp3/feature5',
    pattern: '/miniapp3/feature5/my-feature'
  },
  {
    name: 'Feature 6',
    page: 'miniapp3/feature6',
    pattern: '/miniapp3/my-route/my-subroute/feature4'
  }
];
module.exports = APP_ROUTES;
```
Just keep in mind, that while creating the features, create folders with names feature1 having index.js file rather than having a feature1.js in pages folder.
<Enter>
<Enter>
Now, we just need to write some minor logic in next.config.js in order to read only specific routes needed for each miniapp.
*next.config.js*
In the top of the file we need to add:
```javascript
const MINIAPPS = require('./miniapp');
const APP_ROUTES = require('./routes-list');
const miniappToBeBuild = process.env.APP_NAME;
const basePath = __dirname;
const subDir = NODE_ENV === 'production' ? (miniappToBeBuild ? MINIAPPS[miniappToBeBuild].subPath : '/mainsubPath') : '';

if (miniappToBeBuild && MINIAPPS[miniappToBeBuild]) {
  console.log('MINIPP NAME ---> ', process.env.APP_NAME);
  console.log('MINIPP Subpath ---> ', MINIAPPS[process.env.APP_NAME].subPath);
}
const getExportPaths = () => APP_ROUTES.filter((appRoute) => {
  const filterFlag = MINIAPPS[miniappToBeBuild].patterns.filter((appPattern) => appRoute.pattern.indexOf(appPattern) === 0);
  return filterFlag.length > 0;
});
process.env.SUB_DIR = subDir;
```
<Enter>
and in the *module.exports* section we need to add the below code.
```javascript
module.exports = {
  assetPrefix: subDir,
  async exportPathMap() {
    const paths = {};
    let dynamicSection = '';
    let exportRoutes = APP_ROUTES;
    if (miniappToBeBuild && MINIAPPS[miniappToBeBuild]) {
      console.log(`Building miniapp-${miniappToBeBuild} with subpath-${MINIAPPS[miniappToBeBuild].subPath}`);
      exportRoutes = getExportPaths();
    }
    exportRoutes.forEach((routes) => {
      paths[routes.pattern] = { page: routes.pattern };
    });
    return paths;
  },

  generateBuildId: async () => version,
  webpack: (config, { isServer }) => {
    return config;
  }
};
```
So basically, the exportPathMap function is provided by next js incase you don't want to read your routes from the pages folder and want to have a custom logic for reading the routes. So In that function we added the logic that whatever APP_NAME we pass, only the routes starting with that APP_NAME gets created in the build. (One minor drawback of this approach is that rather than just the required all js files get created, but that's not harming at all as none of them is linked in the scripts of required HTML's)
<Enter>
<Enter>
At last, we just need to write scripts, to pass the APP_NAME and to create separate builds for each miniapp. 
Something like this: -

*package.json*
```javascript
"scripts": {
    "export:minapp1": "npm run clean && cross-env APP_NAME=minapp1 npm run build && cross-env APP_NAME=minapp1 next export && shx mv out/minapp1/* out/ && shx rm -r out/minapp1",
    "export:minapp2": "npm run clean && cross-env APP_NAME=minapp2 npm run build && cross-env APP_NAME=minapp2 next export && shx mv out/minapp2/* out/ && shx rm -r out/minapp2",
    "export:minapp3": "npm run clean && cross-env APP_NAME=minapp3 npm run build && cross-env APP_NAME=minapp3 next export && shx mv out/minapp3/* out/ && shx rm -r out/minapp3",
    "dev": "npm run build && next dev",
    "build": "NODE_ENV=production next build",
    "clean": "rimraf node_modules/.cache .next",
  }

```
To run the above commands on both mac and Windows 2 libraries were required. So pre-requisite: *_npm i cross-env shx_*


So thats all folks, By adding the above minor snippets your micro frontend is ready. In case you want to add another tech miniapp, just create a new repo with that tech and in the build system update the subpath from '' to 'mainSubpath/miniapp4'.

In later posts, I'll show how I created CD/CD pipelines using Azure devops.

### Conclusion
Micro frontends is a better architectural approach if you have a huge repo, getting difficult to manage and faster deliveries are required, which are supposed to be independent for each other.


Thats all folks !!