モノレポでいこう！~yarn workspacesを使ってみた~

## モノレポとは
### 概要
単一のリポジトリで複数のプロジェクトを管理することをモノレポという。
その分リポジトリの内容は複雑になるので、そこそこ運用知識が必要となる。

## メリット、デメリット
モノレポのメリット、デメリットは下記のようなものが挙げられる。

### メリット
- 依存パッケージの管理をまとめてできる
- 共有リソースを有効に利用する
- npm scriptの一括操作

### デメリット
- Issue や PR をすべて 1 つのリポジトリで管理する必要がある
- モノレポでの開発ワークフローを理解している必要がある

## モノレポ作成
### 今回の背景
- Next.jsでHeadressCMSなプロジェクトを作成する
- 検証環境（SSR）、本番環境（SSG）とする
- リポジトリ1つに検証環境と本番環境をまとめて管理したい（リポジトリは分けない）
- ホスティンング環境はAWS Amplifyを使用する

### パッケージ管理ツール選定
パッケージ管理ツールとしてNx、Lernaなど機能がリッチなものもあるが、今回は検証用であり、そこまで高度なことは求めていないので、より低レベルな機能を持つ`yarn workspaces`を使用する。

### yarn workspaces
Yarn1.0以降でデフォルトで使用できるパッケージアーキテクチャを設定するための新しい手法。
下記のようなことが可能。
- 複数 npm パッケージを単一リポジトリで一元管理
- yarn install コマンドを使った Monorepo すべての依存パッケージを一括インストール
- yarn workspaces コマンドを使った Monorepo で管理しているパッケージが持っている npm-scripts の一括実行
- 単一のyarn.lockファイルですべての依存関係を管理

参考：https://classic.yarnpkg.com/lang/en/docs/workspaces/

### 手順
1. リポジトリのルートにpackage.json(プライベート・パッケージ)を作成
`yarn init -p`

　1-1. 下記のようなpackage.jsonが作成される

`private`を`true`としている。

```package.json
{
  "name": "monorepo",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true
}
```

　1-2. 作成するワークスペースを定義する

`workspaces`に`/packages`配下をglobパターンで指定する

```package.json
{
  "name": "monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}
```


2.リポジトリのルートにpackagesディレクトリを作成
```
├── /packages
└── package.json
```
3.ワークスペースを作成 
　3-1. packagesディレクトリ配下にcommon（共通ライブラリ）を作成

```
├── /packages
│   └── /common
└── package.json
```

　3-2. commonにpackage.jsonを作成
`cd packages/common && yarn init`
```
├── /packages
│   └── /common
│   　　└── package.json
└── package.json
```
**name**がワークスペース名となる
```common/package.json
{
  "name": "@projects/common",
  "version": "1.0.0",
  "main": "index.ts"
}
```

　3-3. packagesディレクトリ配下にprd（Next.js本番環境）を作成
`yarn create next-app /packages/prd`
```
├── /packages
│   └── /common
│   │ 　└── package.json
│   └── /prd
│     　└── package.json
└── package.json
```

　3-4. ワークスペース名を編集する
```prd/package.json
{
  "name": "@projects/prd",
  "version": "1.0.0"
  ## 以下省略
}
```
　3-5. packagesディレクトリ配下にstg（Next.js検証環境）を作成
`yarn create next-app /packages/stg`
```
├── /packages
│   ├── /common
│   │ 　└── package.json
│   ├── /prd
│   │ 　└── package.json
│   ├── /stg
│     　└── package.json
└── package.json
```

　3-6. ワークスペース名を編集する
```stg/package.json
{
  "name": "@projects/stg",
  "version": "1.0.0"
  ## 以下省略
}
```

4.common（共通ライブラリ）をprd、stgから参照できるようにする

```package.json
{
　## 以上省略
  "dependencies": {
    "@projects/common": "*"
  }
  ## 以下省略
}
```

5.各ワークスペースのpackage.jsonのパッケージをルートのpackage.jsonに転記する

※Amplifyの仕様上、`dependecies`の`next`は各Next.jsプロジェクトのルート直下のpackage.jsonに記述されている必要がある。（Amplify側がNext.jsプロジェクトかの判別に使っていると思われる）

```package.json
{
　## 以上省略
  "dependencies": {
    "@projects/common": "*",
    ## 各ワークスペースのパッケージを転記する
  },
 "devDependencies": {
    ## 各ワークスペースのパッケージを転記する
  }
  ## 以下省略
}
```


6.各scriptを記述する
リポジトリのルートから各ワークスペースのscriptの実行、またはワークスペース全てにscript実行できるようにpackage.jsonにscriptを記述する。


```package.json
{
  "scripts": {
    "build-all": "yarn build:prd & yarn build:stg",
    "build:prd": "yarn workspace @project/prd build",
    "build:stg": "yarn workspace @project/stg build",
    "dev-all": "yarn dev:prd & yarn dev:stg",
    "dev:prd": "yarn workspace @project/prd dev",
    "dev:stg": "yarn workspace @project/stg dev",
    "start-all": "yarn start:prd & yarn start:stg",
    "start:prd": "yarn workspace @project/prd start:local",
    "start:stg": "yarn workspace @project/stg start:local",
    "lint-all": "yarn workspaces run lint",
    "lint:prd": "yarn workspace @project/prd lint",
    "lint:stg": "yarn workspace @project/stg lint",
    "lint:common": "yarn workspace @project/common lint"
  }
}

```

### Amplifyでのモノレポ運用
Amplifyはモノレポに対応しており、下記のようなことが可能。

- モノレポのサブフォルダを接続する際のビルド設定の自動検出
- 特定のアプリプロジェクト内でコードが変更された場合にのみビルドトリガーする
- 単一のビルド仕様ファイル（amplify.yml）で複数のアプリのビルド設定を定義する

Amplifyのビルド設定には、モノレポのルートに`amplify.yml`を作成する。
今回はprdとstgをそれぞれAmplifyでホスティングする。
下記のように`amplify.yml`を定義する。

```amplify.yml
version: 1
applications:
  - appRoot: packages/prd
    frontend:
      phases:
        preBuild:
          commands:
            - cd ../../
            - yarn
        build:
          commands:
            - cd packages/prd
            - yarn build
      artifacts:
        baseDirectory: out
        files:
          - '**/*'
      cache:
        paths:
          - ../../node_modules
          - ./node_modules
          - .next/cache/**/*
  - appRoot: packages/stg
    frontend:
      phases:
        preBuild:
          commands:
            - cd ../../
            - yarn
        build:
          commands:
            - cd packages/stg
            - yarn build
      artifacts:
        baseDirectory: .next
        files:
          - '**/*'
      cache:
        paths:
          - ../../node_modules
          - ./node_modules
          - .next/cache/**/*
```



参考：
[AWS Blog: Set up continuous deployment and hosting for a monorepo with AWS Amplify Console](
https://aws.amazon.com/jp/blogs/mobile/set-up-continuous-deployment-and-hosting-for-a-monorepo-with-aws-amplify-console/)

[AWS Doc: Monorepo build settings](https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/monorepo-configuration.html)