Micro-app architecture using Angular
Tags: angular, microapps, angularelements
Published: 2019-08-22 20:08:20


---
title: Micro-app architecture using Angular
published: true
description:  
tags: #angular, #microapps, #angular-elements
cover_image: https://thepracticaldev.s3.amazonaws.com/i/igdjw8oy49aswgvhmqbk.jpg
---

Based on the requirement and design, multiple feature apps are developed by independent teams which I call Micro-apps. These micro-apps are placed in a parent application which I call Container app. This Container app decides which micro-app needs to be shown and when. A container will be a template with an empty body which will have some of the basic features in them such as a header and a footer that can be common across the site. Based on the feature micro-apps will be inserted in this empty body. This kind of architecture can be used in an Enterprise application.  

![Micro-app architecture](https://thepracticaldev.s3.amazonaws.com/i/igdjw8oy49aswgvhmqbk.jpg)

Here I am going to use Angular-Cli to achieve this use-case. 


## Building Micro-app
For building the micro app using Angular-Cli, I will use the **custom-webpack** plugin to override bundling configuration. I will also use Angular Elements to load multiple Angular applications on the page.  
#### Angular-Element application
1. Create a new Angular micro-app. For example, here I am using the name *`testOne`*. 
	```console
	ng new testOne
	```		
2. Now I am going to add @angular/element into the workspace.
	```
	cd testOne
	ng add @angular/elements
	``` 
3. Generate a new component with `app-test-one` selector.
	```
	ng g c test-one
	```
5. Override bootstrap of AppComponent in App Module. In `ngDoBootstrap` method, create a custom element. 
	```javascript
	// app.module.ts
	import { BrowserModule } from '@angular/platform-browser';  
	import { NgModule, Injector } from '@angular/core';  
	import { AppRoutingModule } from './app-routing.module';  
	import { TestOneComponent } from './test-one/test-one.component';  
	import { createCustomElement } from '@angular/elements';  
	@NgModule({  
		declarations: [TestOneComponent],  
		imports: [BrowserModule, AppRoutingModule],  
		entryComponents: [TestOneComponent],  
		providers: [],  
		bootstrap: []  
	})  ![enter image description here](https://thepracticaldev.s3.amazonaws.com/i/plxg0wck6ctw2ta4sabc.jpg)
	export class AppModule {  
		constructor(private injector: Injector) {}  
		ngDoBootstrap() {  
			const TestOneElement = createCustomElement(TestOneComponent, {  
				injector: this.injector  
			});  
			if (!customElements.get('app-test-one')) {  
				customElements.define('app-test-one', TestOneElement);  
			}  
		}  
	}
	```
6. Replace `<app-root></app-root>` in index.html to `<app-test-one></app-test-one>`. 

#### Override WebPack Configuration
When Angular-Cli builds a production build using `ng build --prod`, it does not give a unique global variable to the application. Due to this, multiple Angular elements application will conflict each other and you would see an error something like *`Failed to execute 'define' on 'CustomElementRegistry'`*. To avoid this, you would need to assign a variable name to the build generated. We can do this using `@angular-builders/custom-webpack:browser` and provide webPack properties to override it. 

 1. Add `@angular-builders/custom-webpack` plugin. 
 `npm i -D @angular-builders/custom-webpack`
 2. Couple of changes to be done in angular.json 
			 - change the builder property to **_@angular-builders/custom-webpack:browser_**.
			 - add ***`customWebpackConfig`*** property as below.
	   ```
	 "architect": {
		"build": {
			"builder": "@angular-builders/custom-webpack:browser",
			"options": {
				"customWebpackConfig": {
					"path": "./extra-webpack.config.js",
					"mergeStrategies": { "externals": "replace" }
				},
			"outputPath": "dist",
	```
3. Create config file **`extra-webpack.config.js`**  to provide a unique variable name to the bundle generated. 
	```javascript
	module.exports  = {
		output: {
			jsonpFunction:  'webpackJsonpAPPNAME',
			library:  'APPNAME'
		}
	};
	```

	So when you run `ng build --prod`, it will generate `main.js` with content like below

	```javascript
		// considering APPNAME as testOne
		var testOne=(window.webpackJsonptestOne=window.webpackJsonptestOne||[]).
	```

#### Bundle the script 
There are multiple ways to combine script files to one. I am using gulp here. 

1. Install gulp and gulp-concat which is a plugin to concat files. 

	```
	npm i -D gulp gulp-concat
	```

2. Create a gulpfile.js and add the below code. 

	```javascript
	const gulp = require('gulp');  
	const concat = require('gulp-concat');  
	  
	gulp.task('concat', function() {  
		return gulp  
		.src([  
			'./dist/runtime-es2015.js',  
			'./dist/polyfills-es2015.js',  
			'./dist/main-es2015.js'  
		])  
		.pipe(concat('APPNAME.js'))  
		.pipe(gulp.dest('./dist'));  
	});
	```
3. Execute gulp command to execute the process of combining files. 
	```
	gulp concat
	```
**Template Example repo - https://github.com/pranesh229/micro-apps-template**

## Building container app
A container is a regular Angular application which will get hosted in the server and serve the site. The micro-app script files need to be included as a plugin. Below are the steps that need to be followed.
1. Copy the micro-app scripts to the assets folder. For eg - test-one.js can be added to the assets folder. 
2. Call the element in the below way where it needs to be included in the page. For eg - page1.component.ts needs test-one element inside it, the following script needs to be added in `ngOnInit()` method. 
	```javascript
	export class Page1Component implements OnInit {  
		constructor(  
			private renderer: Renderer2,  
			private el: ElementRef,  
			@Inject(DOCUMENT) private document: Document  
		) {}  
		ngOnInit() {  
			// APP 1  
			const appTestOne = this.renderer.createElement('app-test-one');  
			this.renderer.appendChild(this.el.nativeElement, appTestOne);  
			const s = this.renderer.createElement('script');  
			s.type = 'text/javascript';  
			s.src = './assets/test-one.js';  
			this.renderer.appendChild(this.document.body, s);  
		}  
	}
	```

Now when I serve the site, it will append the script and show the component in the page. 

**Container Example repo - https://github.com/pranesh229/micro-app-container**

----------

Thank you for reading.