---
title: 'Angular Workspaces: Multi-Application Projects'
url: https://blog.octoperf.com/angular-workspaces-multi-application-projects/
date: '2019-08-22'
authors:
- gpereira
categories:
- Development
tags:
- Angular
- Tutorial
- Proxy
- SCSS
description: How to create an Angular 8 project with multiple applications and libraries?
---

# Angular Workspaces: Multi-Application Projects

**This blog post is a guide for every developer that would like to create an [Angular Workspace](https://angular.io/guide/glossary#workspace) with several applications and libraries.**

It starts by explaining [how to generate such project with Angular CLI](#angular-multi-application-project-creation-tutorial),
then continues with [shared resources](#angular-multi-application-shared-resources) and [unit testing](#angular-workspaces-and-unit-testing),
to finish by giving [tips on deployment](#how-to-serve-multiple-angular-applications-with-haproxy).


## Angular Multi-Application Project Creation Tutorial 

First, let's start by defining what is an Angular Workspace: 

> A workspace is a set of Angular applications and libraries.
> The `angular.json` file at the root level of an Angular workspace provides workspace-wide and project-specific (application or library) configuration defaults for build and development tools.

Hopefully, an Angular workspace and its configuration files can be generated using the [Angular CLI](https://angular.io/cli).
That's the subject of this chapter: **how to use the Angular CLI to generate a workspace and its applications and libraries**.

Please keep in mind that in Angular terminology, a project can be both an application or a library. 
Both are stored in the `projects` folder of the workspace and configured in the central `angular.json` file. 

### Pre-requisites

**Angular requires Node.js version `10.9.0`** or later (run `node -v` to check the current version).
You can download NodeJS from [nodejs.org](https://nodejs.org/en/) or [install it using a package manager](https://nodejs.org/en/download/package-manager/).

The Angular CLI and generated Angular applications both depend on external libraries that are available as _npm packages_. 
An npm package manager is included with Node.js.
Run `npm -v` to check the npm version.
I suggest to **update npm to the latest available version** (6.10.0 at the time of this blog post) with the command `npm install -g npm@latest`.

Finally, **you need Angular Cli**. Install it globally with the following command:

```shell
npm install -g @angular/cli
``` 

### Create a Workspace

**The [`ng new <name>`](https://angular.io/cli/new) command is used to generate workspaces:**

```shell
ng new kraken --createApplication=false --directory=frontend --interactive=false
```

* The `--createApplication=false` parameter avoids the creation of an initial application (default value is true).
    Otherwise, the Angular CLI creates an application in the `src` folder of the new workspace. 
    It's cleaner to [generate all applications](#create-an-application) in the sub-folder `projects` of the workspace.
* The `--interactive=false` parameter is just here to avoid being prompted for useless parameter values such as whether the initial app (which we don't generate) should include a routing module or what CSS preprocessor to use.
* The `--directory=frontend` parameter is the directory name to create the workspace in. 
    It defaults to the workspace name. 

![Create Angular Workspace](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-cli-new-workspace.png "Create Angular Workspace")

As we can see in the above screenshot, several files are generated by this command in the `frontend` folder.

The first one is [the Angular Workspace configuration file](https://angular.io/guide/workspace-config) `angular.json`.
For the moment it only contains information regarding the location of projects (applications and libraries).
It will be much complex once we have generated some.

```tson
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {}
}
```
Then, the [`package.json` file](https://docs.npmjs.com/creating-a-package-json-file) lists all the dependencies required by Angular.
As the `ng new` command also install the npm dependencies, a [package-lock.json](https://docs.npmjs.com/files/package-lock.json) file is generated as well as a [node_modules](https://docs.npmjs.com/files/folders#node-modules) folder that contains the downloaded dependencies.

The [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) file specifies TypeScript compiler options (Angular projects are written in TS).
And the [tslint.json](https://palantir.github.io/tslint/usage/configuration/) file configures the TypeScript [Linter](https://en.wikipedia.org/wiki/Lint_(software)), a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs.

Finally, a `README.md` file is also generated. 
Read it to get information related to the Angular CLI usage: how to build, serve and test the newly generated application.

### Create an Application

**The [`ng generate application <name>`](https://angular.io/cli/generate#application) command is used to create a new application in the `projects` sub-folder of the workspace.**


* The parameter `--style=scss` sets the [SCSS](https://sass-lang.com/documentation/syntax#scss) preprocessor to use for style files (default is CSS).
* The parameter `--routing=true` tells Angular CLI to generate a routing [NgModule](https://angular.io/guide/module-types).

Run the following commands to generate two applications, Administration and Gatling:

```shell
cd frontend
ng generate application administration --style=scss --routing=true
ng generate application gatling --style=scss --routing=true
```

![Create Angular Application](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-cli-generate-application.png "Create Angular Application")

These commands create files for each application in the sub-folders `projects/administration` and `projects/gatling`:

* Two `tsconfig.*.ts` files. 
    They extend the workspace root `tsconfig.ts` and set specific configuration to compile the application (`tsconfig.app.ts`) or its unit tests (`tsconfig.spec.ts`).
    Read more about TypeScript configuration on [Angular documentation](https://angular.io/guide/typescript-configuration).
* The `src` folder, it contains all TypeScript, HTML and CSS sources for your application (More on the [`src` folder structure](https://angular.io/guide/file-structure#multiple-project-file-structure)).
* The `karma.conf.js` Karma configuration file used for [unit tests](#angular-workspaces-and-unit-testing).
* The `e2e` folder for running [End-To-End tests with protractor](https://angular.io/cli/e2e).  


It also updates the root `angular.json` file, adding the two applications:

```tson
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "administration": {
      "projectType": "application",
      [...]
    },
    "gatling": {
      "projectType": "application",
      [...]
    }
  },
  "defaultProject": "administration"
}
```

We will come back later on [the configuration specific to each application project](https://angular.io/guide/workspace-config#overall-json-structure) as we will have to update them in order to [share resources](#angular-multi-application-shared-resources). 

### Create a Library

**The [`ng generate library <name>`](https://angular.io/cli/generate#library) command is used to generate libraries.**

In the workspace folder (`frontend` if you followed this tutorial to the letter), run these commands:

 ```shell
ng generate library tools
ng generate library vendors
 ```

![Create Angular Library](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-cli-generate-library.png "Create Angular Library")

Here again, the `angular.json` file is updated with the two newly created libraries:

```tson
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "administration": {
      [...]
    },
    "gatling": {
      [...]
    },
    "tools": {
      "projectType": "library",
      [...]
      }
    },
    "vendors": {
      "projectType": "library",
      [...]
    }
   },
  "defaultProject": "administration"
}
```

It also [creates files for each library](https://angular.io/guide/file-structure#library-project-files) in the `projects/tools` and `projects/vendors`. 
They are mostly similar to the ones [generated for applications](#create-an-application).

One thing to note is that you **cannot specify the style preprocessor when generating a library**.
And there is no configuration related to it in the workspace configuration (`angular.json` file) for the libraries.
The result is that you must specify the style each time you generate a component in a library, for example with the command `ng generate component my-component --style=scss --project=tools`.

### Create a Shared Service

**The idea here is to generate a service inside a library, and use it in an application.**

Let's create a dummy service in the `tools` library:

```shell
ng generate service hello-world --project=tools
```

The syntax is `ng generate service <service-name>` and the parameter `--project` is mandatory to specify the library where to generate the service.

![Create Angular Service](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-cli-generate-service.png "Create Angular Service")

This creates a file `projects/tools/src/hello-world.service.ts` and its unit test (`projects/tools/src/hello-world.service.spec.ts`).
Update it to create a simple getter:

```ts
import {Injectable} from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class HelloWorldService {
  get message(): string {
    return 'Hello World!';
  }
}
```

> __Note:__ The service is declared as injectable with the `providedIn: 'root'` option.
> That means this service is a singleton. It can be used in any component or service without having to provide it.
> It is always the same instance, anywhere you use it.
>
> Without this option, we could have injected one instance for each component using it with the syntax `@Component({providers: [HelloWorldService]})`.
> We could also do the same on the module level with the syntax `@NgModule({providers: [HelloWorldService]})`.
>
> Read more about dependency injection in [Angular documentation](https://angular.io/guide/dependency-injection).


Update the AppComponent `projects/administration/src/app/app.component.ts` to use this service:

```ts
import {Component} from '@angular/core';
import {HelloWorldService} from 'projects/tools/src/lib/hello-world.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'administration';

  constructor(helloWorld: HelloWorldService) {
    this.title = helloWorld.message;
  }
}
```

**If you are using IntelliJ Idea or Webstorm**, the default import for the HelloWorld service is `import {HelloWorldService} from '../../../tools/src/lib/hello-world.service';`.
That's ugly and hard to maintain if you move files around. 
You can open the project settings and select the _TypeScript > Imports > 'Use path relative to tsconfig.json'_ option:

![Idea Import Settings](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/idea-import-settings.png "Idea Import Settings")

### Install Angular Material

[Angular Material](https://material.angular.io/) is a set of components that follows the conventions set by [Material Design](https://material.io/design/). 

Installing it is done in no time thanks to the Angular CLI.
Simply run the following commands to install Angular Material on both applications.

```shell
ng add @angular/material
ng add @angular/material --project=gatling
```

> __Note:__ We do not need to specify the `--project=administration` for the first command as it is the default project in the workspace configuration
> (`"defaultProject": "administration"` in the `angular.json` file).

![Add Angular Material](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-cli-add-material.png "Add Angular Material")

These commands add the dependency `@angular/material` in the `package.json` file and also automatically update various project files,
adding the proper styles, importing the fonts and NgModules:

```shell
UPDATE projects/administration/src/main.ts (391 bytes)
UPDATE projects/administration/src/app/app.module.ts (502 bytes)
UPDATE angular.json (10132 bytes)
UPDATE projects/administration/src/index.html (482 bytes)
UPDATE projects/administration/src/styles.scss (181 bytes)
```

**I find it cleaner to regroup external dependencies into a single NgModule.**
That's why we previously created a `vendors` library.

So let's update the VendorsModule file `projects/vendors/src/lib/vendors.module.ts` to import and export the MatButtonModule,
thus regrouping our external dependencies in a single place:

```ts
import {NgModule} from '@angular/core';
import {MatButtonModule} from '@angular/material';

@NgModule({
  imports: [MatButtonModule],
  exports: [MatButtonModule]
})
export class VendorsModule { }
```

Then, we import our VendorsModule in our AppModule `projects/administration/src/app/app.module.ts`:

```ts
@NgModule({
  [...]
  imports: [
    VendorsModule,
  ],
  [...]
})
export class AppModule { }
```

Finally, use the `mat-raised-button` directive in the AppComponent `projects/administration/src/app/app.component.html`:

```html
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <div><img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,..."></div>
  <button mat-raised-button color="primary" >Click Me!</button>
</div>
```
Head to the [next chapter](#launch-the-applications) to see what it looks like!

### Launch The Applications

**To serve an Angular application, simple run the command `ng serve --project=administration`.**

You can then open the web page at http://localhost:4200/:

![Angular Material Button](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/angular-material-button.png "Angular Material Button")

If you followed every step of this tutorial, you should see the _"Welcome to Hello World!!"_ message as well as the raised Material _"Click Me!"_ button.
You may otherwise [download the source code](https://blog.octoperf.com/img/blog/angular-workspace-multi-application-project/frontend.zip) (simply run `npm install` before launching the application).

**If you want to have a look at a more complex application, please head to the [Complete Application Sample](#complete-application-sample) chapter.**

Also note that if you want to launch both `administration` and `gatling` applications at the same time, you will see an error message:

```shell
Port 4200 is already in use. Use '--port' to specify a different port.
Error: Port 4200 is already in use. Use '--port' to specify a different port.
```

Either use the `--port` option or read the [How To Serve Multiple Angular Applications With HAProxy](#how-to-serve-multiple-angular-applications-with-haproxy) chapter to learn how to set the default port used by an application.

## Angular Multi-Application Shared Resources

It's always nice to split a big application into several modules and libraries.
**It eases the maintenance and improves code [reusability](https://en.wikipedia.org/wiki/Reusability).**

But regarding reusability, sharing CSS styles and assets across component libraries is pretty handy.
It might be cleaner to have completely isolated modules, but since all my libraries and applications rely on Angular Material, I do not want to duplicate the import code for each project.  

### Sharing CSS Across Libraries and Applications

In the [previous chapter](#angular-multi-application-project-creation-tutorial) we [imported one of the default Angular Material themes](https://material.angular.io/guide/getting-started#step-4-include-a-theme).
I wanted to use a [custom Angular Material theme](https://material.angular.io/guide/theming) with:

* A dark background,
* More colors than the base primary and accent (adding warning, success, danger, and other colors),
* A more dense layout of the components ([multi-level density is not available yet](https://github.com/angular/components/issues/4597)).

**To create this UI, I had to create several component libraries:**

* Resizable split panels,
* Tabs for the console and file trees,
* Code editors,
* And workspaces that combine all of these.

They all use common CSS located at the root of the workspace, in the styles folder.
For example, the custom material theme SCSS file initializes Material Design and declares custom colors:

```css
@import '../node_modules/@angular/material/theming';

[...]

$app-primary: mat-palette($mat-blue);
$app-accent: mat-palette($mat-green);
$app-info: mat-palette($mat-light-blue);
$app-success: mat-palette($mat-light-green);
$app-error: mat-palette($mat-deep-orange);
$app-background: mat-palette($mat-gray);

[...]

$mat-theme: (
  [...]
);

@include angular-material-theme($mat-theme);
```

And the compact Scss file declares custom CSS classes used to change [components density](https://material.io/design/layout/applying-density.html#usage):

```css
@import 'app-padding';
@import 'app-font';

// Must also disable the ripple effect
@mixin compact-checkbox($size: 34px) {
  $half: $size / 2;
  .mat-checkbox-inner-container {
    height: $half;
    width: $half;
  }
}

@mixin compact-button($size: 34px) {
  .mat-icon-button {
    width: $size;
    height: $size;
    line-height: $size * 0.9;

    .ng-fa-icon {
      font-size: $size * 0.5;
    }
  }
}
[...]
```

**In order to use these SCSS files in our Administration and Gatling Load Testing applications, we must declare them in Angular's workspace configuration `angular.json`:** 

```tson
"stylePreprocessorOptions": {
  "includePaths": [
    "styles"
  ]
},
```
The `stylePreprocessorOptions` must be added into `projects > administration / gatling > architect > build > options`.

While [running unit tests](#angular-workspaces-and-unit-testing), you may get the following error if some components under test need the common styles: 

```shell
ERROR in ../icon/src/lib/icon/icon.component.scss                                                                                                                   
Module build failed (from /home/kojiro/workspaces/kraken/frontend/node_modules/sass-loader/lib/loader.js):

@import 'app-margin';
       ^
      Can't find stylesheet to import.
```

In this case, simply add the `stylePreprocessorOptions` to into the section `projects > my-project > architect > test > options` of the workspace configuration.

> **Note:** I'm used to [Bootstrap](https://ng-bootstrap.github.io/#/home). Its theme offers more colors than Material with [primary, success, error, etc](https://ng-bootstrap.github.io/#/components/alert/examples#closeable).
> I like Angular Material but I think having only a _primary_ and an _accent_ colors is not enough for complex applications. 
> If it's not the case for you, you can create component libraries while staying separated from the Angular Material theme used: [Theming Your Components Guide](https://material.angular.io/guide/theming-your-components).
>
> You might want to limit the usage of common CSS classes in such case.

### Sharing Assets Across Libraries and Applications

**Along with commons CSS files, you may also want to share assets across your applications and libraries.**

For example:

* The application logo,
* Javascript files used by [Ace editor](https://ace.c9.io/),
* etc.

In fact, the Ace theme and mode files are located in the ext folder and copied thanks to the `angular.json` configuration.
For instance, the Gatling `build` architect options include the following configuration:

```json
"assets": [
  "projects/gatling/src/favicon.ico",
  "projects/gatling/src/assets",
  {
    "glob": "**/*",
    "input": "assets/",
    "output": "assets/"
  },
  {
    "glob": "**/worker-*.js",
    "input": "node_modules/ace-builds/src-min/",
    "output": "assets/ace/"
  },
],
```
It first declares assets specific to the Gatling application.
Then **it includes all files in the root _assets_ folder with the `"glob": "**/*"` syntax**.
Finally, all [Ace workers](https://github.com/ajaxorg/ace/wiki/Syntax-validation) are copied in `assets/ace/`.

We also use many other javascript files related to the Ace code editor. 
These themes, modes, and snippets must be declared in the _scripts_ section of the `build` architect options: 

```json
"scripts": [
  "node_modules/ace-builds/src-min/mode-xml.js",
  "node_modules/ace-builds/src-min/mode-yaml.js",
  "node_modules/ace-builds/src-min/ext-searchbox.js",
  "node_modules/ace-builds/src-min/ext-language_tools.js",
  "node_modules/ace-builds/src-min/ext-modelist.js",
  "ext/mode-log.js",
  "ext/theme-kraken.js",
  "ext/snippets/scala.js"
]
``` 

No matter what you want to achieve with assets and external scripts, **I suggest you have a look a this Angular-Cli documentation page: [Stories Asset Configuration](https://github.com/angular/angular-cli/wiki/stories-asset-configuration)**. 

## Angular Workspaces and Unit Testing

The Angular CLI allows you to [run unit tests on a specific project](https://angular.io/cli/test) with the syntax:

```shell
ng test --project=<my-project>
```

* By default it only runs the unit tests continuously, letting you know if they succeed or fail. 
    Use `fit` instead of `it` on a unit test definition to run only this one particular test (You can also use `fdescribe` on a test suite).
* It automatically generates [test coverage](https://en.wikipedia.org/wiki/Code_coverage) reports in the folder `/coverage/my-project` with the options `--watch=false --codeCoverage=true`. 

**I wanted to have a single report that regroups all coverage information, as well as speed up test executions by running unit tests in parallel.**
So I created the following script:

```shell
#!/bin/bash
rm -rf coverage
rm -rf coverage-all

for dir in projects/*; do
  if [["$dir" != *-e2e]]
  then
    prefix="projects/";
    project=${dir#$prefix}; #Remove prefix
    echo "$project"
    ng test --watch=false --codeCoverage=true --sourceMap=true --project=$project &
  fi
done

wait  # Wait for all tasks to complete

./node_modules/istanbul/lib/cli.js report --dir ./coverage-all/ html

google-chrome ./coverage-all/index.html &
```

It runs unit tests with coverage in parallel for each project not ending with `-e2e` (these are [end-to-end](https://angular.io/cli/e2e) test projects).
Then, all reports are aggregated using [Istanbul client](https://istanbul.js.org/).

> __Warning:__ If you have many projects, this script will run several web browsers at the same time.
> It may freeze your computer while it is running!

Istanbul needs report in the JSON format in order to aggregate them.
So, update the `karma.conf.js` files as well. 
They are located at the root of every application or library. 
Simply add the `json` report:

```ts
coverageIstanbulReporter: {
  dir: require('path').join(__dirname, '../../coverage/analysis'),
  reports: ['html', 'lcovonly', 'json'],
  fixWebpackSourcePaths: true
},
```

Take a look at [Angular documentation about testing](https://angular.io/guide/testing) if you want to know how to write unit tests.

## How To Serve Multiple Angular Applications With HAProxy 

The idea is to serve two applications on different ports and base Href; and then to package them as docker images served on the same port but using different base Href.  

### Serving Angular Apps on Specific Port and Base Href

By default, Angular serves all applications on the same url and port: http://localhost:4200.

To be able **to start two applications at the same time while developing, change the port of one of them in the Angular workspace configuration `angular.json` file**.
For example, the Gatling application has a `serve` architect configured to serve it on port 4222:  

```json
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "browserTarget": "gatling:build",
    "port": 4222
  },
  "configurations": {
    "production": {
      "browserTarget": "gatling:build:production"
    }
  }
},
```

Then, run the `ng serve` command with the following parameters:

```shell
ng serve --open --project=gatling --baseHref /gatling/
```

It will open a web browser at the URL http://localhost:4222/gatling thanks to the `--baseHref` parameter and the `angular.json` configuration.

### Packaging Angular Apps as Docker Images

Angular apps are built thanks to the Angular-Cli [`ng build` command](https://angular.io/cli/build):

```shell
ng build gatling --prod --baseHref /gatling/
```

Here again, the `--baseHref /gatling/` is used to specify the base Href. 
**The application is built in the folder `dist/gatling`.**

The following DOCKERFILE generates an [NGINX Docker image](https://hub.docker.com/_/nginx) with the built application attached:

```shell
FROM nginx:1.15.9-alpine
ARG APPLICATION
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
COPY dist/$APPLICATION .
RUN ls -laR .
EXPOSE 80
```

The output of the `ng build` command is copied into the folder `/usr/share/nginx/html` of the image, and the following Nginx configuration is used:

```shell
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    server {
        listen 80 default_server;
        server_name  localhost;

        root   /usr/share/nginx/html;
        index  index.html index.htm;
        include /etc/nginx/mime.types;

        gzip on;
        gzip_min_length 1000;
        gzip_proxied expired no-cache no-store private auth;
        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;

        location / {
            try_files $uri $uri/ /index.html;
        }
    }
}
```

Run the command below to generate the Docker image:

```shell
docker build --rm=true -t gatling-ui:latest --build-arg APPLICATION=gatling .
```

### Serving Production Images With HAProxy

[HAProxy](https://www.haproxy.org/) is an open-source software that provides a high availability proxy server for TCP and HTTP-based applications.
We use it to redirect HTTP requests on the proper Frontend, depending on the base Href (thanks the `path_beg /administration` keyword).

Here is the configuration file:

```shell
global

defaults
    mode    http
    option  forwardfor
    option  http-server-close
    # Set the max time to wait for a connection attempt to a server to succeed
    timeout connect 30s
    # Set the max allowed time to wait for a complete HTTP request
    timeout client  50s
    # Set the maximum inactivity time on the server side
    timeout server  50s
    # handle the situation where a client suddenly disappears from the net
    timeout client-fin 30s

frontend http-in
    bind *:80
    mode http

    acl has_administration  path_beg /administration
    acl has_gatling  path_beg /gatling

    use_backend administration if has_administration
    use_backend gatling if has_gatling

backend administration
    server administration-ui administration-ui:80 check fall 3 rise 2
    reqrep ^([^\ ]*\ /)administration[/]?(.*)     \1\2

backend gatling
    server gatling-ui gatling-ui:80 check fall 3 rise 2
    reqrep ^([^\ ]*\ /)gatling[/]?(.*)     \1\2
```

The easiest way to start it is using the [HAProxy Docker image](https://hub.docker.com/_/haproxy/).

## Complete Application Sample

I hope this blog post is helpful for anyone that would like to **give a try to Angular for a complex multi-project workspace**.
