ファインディの爆速開発を支えるモノレポ管理ツール「Nx」について

ファインディ株式会社でフロントエンドのリードをしております 新福(@puku0x)です。

この記事では、ファインディで導入しているモノレポ管理ツール「 Nx 」について紹介します。

モノレポとは

モノレポは全てのコードベースを単一のリポジトリで管理する手法です。

monorepo.tools

コードの共通化や可視化、ツール・ライブラリの標準化、一貫性のあるCI/CDパイプラインを構築できるといったメリットがあります。また、マイクロサービスと相性が良いとも言われています。

circleci.com

ファインディでは主にフロントエンド系のリポジトリをモノレポとして運用しています。

アプリケーションとそれに関連するフィーチャー、UIライブラリがひとつにまとまっているため、複数のリポジトリを移動することなくコードを参照できます。

Nxとは

Nxはモノレポ管理やアプリケーションのビルド、テストの実行、コード生成などの機能を備えた統合的なツールです。

nx.dev

モノレポ管理ツールは他にもLernaTurborepoなどがあります。アプリケーション開発において有用な機能が多くあることや、コミュニティが活発で継続的なメンテナンスが見込めること、前職で導入実績があったことなどがNxを選んだ理由です。

後述する「変更検知」や「キャッシュ機構」といった機能は、Pull Requestの粒度を細かくするファインディの開発スタイルと高い親和性があり、導入当初から私達の開発を支える頼もしいツールとなっています。

大手企業でも採用事例が増えており、Nxはモノレポ管理ツールとして有力な選択肢といえるでしょう。

https://npmtrends.com/lerna-vs-nx-vs-turbo

(個人的に推していたOSSがここまで有名になったことには感慨深いものがありますね!)

Nxワークスペースの作成

create-nx-workspace を実行するとワークスペースを作成できます。

Reactをはじめ、メジャーなフレームワーク用のプリセットもあるためすぐに開発を始められます。

npx create-nx-workspace@latest <ワークスペース名> --preset=react-monorepo --appName=<アプリケーション名> --bundler=webpack --e2eTestRunner=playwright --style=scss --nxCloud=skip

詳細は公式のチュートリアルをぜひご覧ください。

nx.dev

Nxの機能

コード生成

nx generate コマンドでプロジェクト(アプリケーションやライブラリ)を作成できます。

npx nx generate @nx/react:application --name=<アプリケーション名> --directory=<ディレクトリ> --routing=false --e2eTestRunner=playwright --projectNameAndRootFormat=as-provided
npx nx generate @nx/react:library --name=<ライブラリ名> --directory=<ディレクトリ> --bundler=rollup --unitTestRunner=jest --projectNameAndRootFormat=as-provided

nx generate で実行できるGeneratorは自分で作ることもできます。

nx.dev

ファインディではフィーチャー用のファイル一式を生成するGeneratorを作って開発を効率化しているチームもあります。また別の機会に紹介できればと思います。

変更検知

Nxはプロジェクト間の依存関係を自動的に算出する機能を備えています。

npx nx affected --graph を実行すると次のように依存関係を可視化できます。

npx nx affected --target=build のように指定すると、変更のあったプロジェクトとそれに依存する他のプロジェクトを全てビルドしてくれます。

nx affected は変更されたコードに関するプロジェクトのみ実行する

個人的なおすすめは、npm-scriptsやCI上で実行するコマンドを nx affected ベースにすることです。

"scripts": {
  "build": "nx affected --target=build",
  "test": "nx affected --target=test",
  "lint": "nx affected --target=lint",
  ...
},

必要なタスクのみ実行されるためスピーディーに開発できます。

依存関係の管理

Nxが持つプロジェクト間の依存関係はLintルールにも応用されます。

ファインディでは、@nx/enforce-module-boundaries ルールを適用し、意図しない依存関係の逆転が起こらないように制御しています。

nx.dev

具体的には、各プロジェクトに次のようなタグを設定し、

{
  "name": "utils",
  "sourceRoot": "libs/utils/src",
  "projectType": "library",
  "tags": ["scope:shared", "type:util"],
  ...
}
- apps/
  - app1                (scope:app1)
  - app2                (scope:app2)
- libs/
  - app1/
    - feature-dashboard (scope:app1, type:feature)
    - ui                (scope:app1, type:ui)
  - app2/
    - feature-user      (scope:app2, type:feature)
    - ui                (scope:app2, type:ui)
  - utils               (scope:shared, type:util)

.eslintrc.json に対応するルールを設定しています。

"@nx/enforce-module-boundaries": [
  "error",
  {
    "allow": [],
    "depConstraints": [
      {
        "sourceTag": "scope:shared",
        "onlyDependOnLibsWithTags": ["scope:shared"]
      },
      {
        "sourceTag": "scope:app1",
        "onlyDependOnLibsWithTags": ["scope:shared", "scope:app1"]
      },
      {
        "sourceTag": "scope:app2",
        "onlyDependOnLibsWithTags": ["scope:shared", "scope:app2"]
      },
      {
        "sourceTag": "type:feature",
        "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
      },
      {
        "sourceTag": "type:ui",
        "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
      },
      {
        "sourceTag": "type:util",
        "onlyDependOnLibsWithTags": ["type:util"]
      }
    ]
  }
]

設定したルールを図にするとこのようになります。scope がプロジェクト間の依存関係、type が内部のレイヤーの依存関係を表します。

@nx/enforce-module-boundaries ルールが設定されたプロジェクトでは依存してはいけないモジュールに対してエラーが表示されます。

大規模なコードベースの管理においては、モジュール同士の依存関係の制御が非常に重要であり、こういった「かゆいところに手が届く」機能を持っていることがNxの魅力でもあります。

キャッシュ機構

Nxは一度実行されたコマンドのキャッシュを保持しており、同じコマンドが実行された場合にキャッシュから結果を再現する機能を持っています。

nx.dev

また、関連サービスである「 Nx Cloud」のリモートキャッシュ機能を有効にすると大幅なCI高速化が可能です。

実際にファインディでは、キャッシュの活用で毎月1,000時間以上のCI時間を削減できました。

自動マイグレーション

NxにはCodemodAngular CLIng update と似たコマンドが備わっています。

nx.dev

npx nx migrate latest
npx nx migrate --run-migrations

nx migrate を実行すると、Nx本体と各種プラグイン、reacteslinttypescript などの依存ライブラリ・ツールがまとめて更新されます。

バージョンアップに伴うマイグレーションは手動で対応すると大変ですが、Nxでは自動化されています。メンテナンスの手間が省けると同時に属人化も抑えられるため重宝しています。

まとめ

この記事では、Nxの概要と基本的な機能について紹介しました。

Nxは単なるモノレポ管理ツールに留まらず、「開発者体験の改善」や「開発生産性の向上」といったポテンシャルを秘めています。

✨✨✨ Nxはいいぞ! ✨✨✨

Nxはいいぞおじさんから伝えたいことは以上です。

より詳細な活用法やNx Cloudの高度な機能については、今後の記事で取り上げる予定ですのでご期待ください。

ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。

herp.careers