複数ユーザー向けSaaSのベストプラクティスを考える feat.Angular

# やりたいこと
エンタープライズ向けのSaaSを構築する上で必ず必要になるマルチユーザー対応。
WEBアプリケーションにおいては、
利用するユーザー（会社）によって挙動・見た目を変える
がいつも課題になっている気がしています。
SPA(Angular)を使った場合、どうしたら開発者もユーザーもハッピーか考えてみる。


# やりかた
以下のやり方が考えられるかなーと思います。
①クライアントに設定を持つ
②サーバに設定を持つ

### ①クライアントに設定ファイルを持つ
Angularの場合だとenvironment.tsのような設定ファイルやSCSSをユーザー別に用意しておき、APIの向き先・挙動などをずらっとかき、ビルド時にユーザーを指定する。

```environment-userA.ts
export const environment = {
	production: true,
	user :"usera",
	baseUrl: 'https://usera.application.jp/api/', // APIのベースURL的な
	setteiA: 'A', 
	setteiB: 'B',
...
};
```

```angular.json
	"configurations": {
		"production": {
			"fileReplacements": [
				{
					"replace": "src/environments/environment.ts",
					"with": "src/environments/environment.prod.ts"
				}
			],
			"optimization": true,
			"outputHashing": "all",
			"sourceMap": false,
			"extractCss": true,
			"namedChunks": false,
			"aot": true,
			"extractLicenses": true,
			"vendorChunk": false,
			"buildOptimizer": true,
			"budgets": [
				{
					"type": "initial",
					"maximumWarning": "2mb",
					"maximumError": "5mb"
				}
			]
		},
		"usera": {
			"fileReplacements": [
				{
					"replace": "src/environments/environment.ts",
					"with": "src/environments/environment.userA.ts"
				}
			],
			"optimization": true,
			"outputHashing": "all",
			"sourceMap": false,
			"extractCss": true,
			"namedChunks": false,
			"aot": true,
			"extractLicenses": true,
			"vendorChunk": false,
			"buildOptimizer": true,
			"budgets": [
				{
					"type": "initial",
					"maximumWarning": "2mb",
					"maximumError": "5mb"
				}
			]
		}
	}
},
"serve": {
	"builder": "@angular-devkit/build-angular:dev-server",
	"options": {
		"browserTarget": "sample:build"
	},
	"configurations": {
		"production": {
			"browserTarget": "sample:build:production"
		},
		"userA": {
			"browserTarget": "sample:build:usera"
		}
	}
}
```

```bash

ng build -c usera
```


<b>PROS</b>
1. クライアントサイドだけで完結できる
2. 設定をクライアントソースに内包できる
<b>CONS</b>
1. ユーザーごとにoutputが増え、ビルドのコストが高い
2. 設定変更だけでクライアントの修正＆再リリースが必要

### ②サーバーに設定を持つ
初回リクエスト時に基底コンポーネントから初期サービスを同期でリクエスト。
FQDNに応じてユーザーをリクエストボディにセットした上で初期サービスを呼び出し、レスポンスに応じてCSSや設定を注入する。

```app.component.ts
import { Component, OnInit, OnChanges } from '@angular/core';
import { SafeHtml, DomSanitizer } from '@angular/platform-browser';
import { InitService } from 'src/app/services/init.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnChanges {
  public safeHtml: SafeHtml;
  constructor(private domSanitizer: DomSanitizer, private initservice:InitService) {

  }

  async ngOnInit(): Promise<void> {
    // 初期設定サービスを同期で呼び出し。
    this.initialsetting = await this.initservice.init();
    // 各ユーザー別のCSSを初回ロードして&inject。
	this.safeHtml = this.domSanitizer.bypassSecurityTrustHtml("<link rel='stylesheet' type='text/css' href=" + this.initialsetting.cssurl + ">");
  }
    // 適当に設定を保持（storeなりsessionStorageなり）
}

```

```initservice.ts
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { InitialSetting } from '../models/initialsetting';

@Injectable({
  providedIn: 'root'
})
export class InitService {
  constructor(
    private http: HttpClient,
  ) { }
  
  async init() {
    // host名からユーザコードを持ってくる。mappingはenvironment.tsとかで。
    const host = location.host;
    const userCd = environment.hostToUserCd[host]
    // なにも認証無いとあれなのでヘッダに認証キーを付けとく。
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'my-auth-token'
      })
    };
    let body = {};
    body['userCd'] = userCd
    const url = 'https://' + host + '/api/initialservice';
    return await this.http.post(url, body, httpOptions)
      .toPromise()
      .then(response => response as InitialSetting)
  }
}

```

```environment.ts
export const environment = {
  production: false,
  hostToUserCd: {
	'usera.application.jp':'usera',
	'userb.application.jp':'userb',
  }
};
```

<b>PROS</b>
1. どんなにユーザがいてもビルド一発で終わる
2. 全ユーザが同じソースで動く（CSS以外）
<b>CONS</b>
1. サーバー側に考慮が必要
2. 初期ロードが若干遅い

# まとめ
ユーザが少なければサーバーとクライアントが疎な①型、ユーザが多い場合は②がよいかな。
他にいい案あれば教えてください。