2022.10.07

Monorepoに移行しました

メンヘラテクノロジーのメインのプロダクトであるメンヘラせんぱいはNuxtとFirebaseで動いています。また、管理画面にはNextを使っており、別のリポジトリで実装しています。今回はこれらをひとつにまとめました

モチベーション

NuxtとFirebaseでプロジェクトをはじめるとルートにNuxt側のsrcディレクトリと一緒にfunctionsというディレクトリが作られると思います<br> Nuxt側とfunctions側でコードを共有できず、型定義やFirestoreへのCRUDを2回書くはめになります。さらに管理画面なんかも作ると似たコードを3回書くことになります(やばい)<br> これだと同じコードを何度も書かされるので無駄が多いですし、片方のアプリで加えた変更をもう一方に取り込み忘れ、アプリ間で実装の誤差が出てきてしまうリスクもあります<br> これらの課題意識から、共通化できるところは共通化してしまいたくなり、monorepoに挑戦しました

ゴール

ディレクトリ構成は次のように形を目指します<br>

menhera
├── packages
│   ├── common: 共通の型定義など
│   ├── console: 管理画面(Next)
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── menhera-senpai: メンヘラせんぱいのフロントエンド(Nuxt)
│   │   ├── package.json
│   │   └── tsconfig.json
│   └── server: cloud functions
│       ├── package.json
│       └── tsconfig.json
├── package.json
├── tsconfig.json
└── yarn.lock

各アプリをパッケージとしてpackagesの中に並べ、各パッケージ内のpackage.jsonに依存モジュールが書かれます。yarn.lockはルートにのみ生成され、これでモジュールをすべて共通で管理することができます<br> monorepoにはyarn workspacesを使い、commonsには共通化したい型定義を書き、commonsをパッケージとしてimportできるようにしました

実際の作業

それでは実際に移ります<br>

初期設定

まずは空のプロジェクトを作ります

$ yarn init

package.jsonを編集します

// ./package.json
{
  "name": "menhera",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "workspaces": {
    "packages": [
      "packages/*"
    ]
  }
}

ルートにtsconfig.jsonを追加します。commonは全packageで共通で使いたいのでパスのaliasはこちらに書いておきます

// ./tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "^@/*": ["./packages/common/*"]
    },
  }
}

ソースコードの移動

それではソースコードを移して、設定していきます<br> アプリのpackage.jsonのdependenciesは消し、yarn addし直します

$ yarn workspace @menhera/menhera-senpai add hogehoge

tsconfig.jsonnuxt.config.jsを編集し、パッケージ間でimportできるようにしていきます<br> nuxt.config.jsの方にもcommonからimportするためのaliasを追記します

// ./packages/menhera-senpai/nuxt.config.js
{
  ...,
  alias: {
    '^@': resolve(__dirname, '../common'),
  }
}

また、package内のtsconfig.jsonはルートのtsconfig.jsonをextendするようにします

{
  "extends": "../../tsconfig.json",
  ...
}

デプロイの設定

最後にデプロイ設定を修正します<br> まずfirebase.jsonをルートに移動し、firebase.json内に書いていた諸々のパスを変更します

// ./firebase.json
{
  ...,
  "hosting": {
    "public": "./packages/menhera-senpai/dist",
    ...
  },
  "functions": {
    "predeploy": [
      "yarn workspace @menhera/server run lint",
      "yarn workspace @menhera/server run build"
    ],
    "source": "packages/server"
  },
}

といった感じになります<br> これでデプロイもできるようになりました🎉

さいごに

monorepoは敷居が高いイメージがあってなかなか挑戦できずにいたので今回できてよかったです<br> これで本当に開発スピードが上がるのかを今後運用してみないとわかりませんが、少なくともアプリ間のギャップや実装漏れは減らせそうなので効果はありそうです