【ESlint】フロントエンドアーキテクチャ上で決めた依存ルールをESlintで制約として表現する

この記事は Wano Group Advent Calendar 2022の23日目の記事となります。

はじめに

改めてですが、私は音楽ディストリビューションサービス「TuneCore Japan」のフロントエンドエンジニアを担当しております。みなさん、フロントエンド開発楽しんでますか。私は相変わらず楽しくやっております🥹

さて、プロダクトTuneCore Japanは クリーンアーキテクチャ を参考にしたアーキテクチャを採用しております(ここについての詳細は割愛)。新規機能の実装などはその構成によせ、また適宜既存箇所の修正をする場合はボーイスカウトルールに乗っ取ってなるべく新しい構成に寄せられるように頑張っております。

各レイヤーの責務・依存関係をチームで擦り合わせた上で実装を進めることができており、バグ発生時の影響範囲の特定がしやすくなったことや、レイヤーごとの責務が明確になったことからコード品質が改善し、スムーズに実装に取り組めるようになったと思います。 

そして、当たり前のことですが実際の実装フェーズにおいては、レビュー担当・実装者は「実装がアーキテクチャに沿っているか」がレビュー観点に含まれることになります。もちろん、各メンバーがアーキテクチャを理解した上で日々の実装に取り組んでいるので大きく構成から外れるようなことはないですが、もちろん人間ですのでミスはします。例えば、APIの型定義をそのままアプリケーション固有のロジック・定義が存在する層で参照してしまうことなど、例を挙げればキリがありません。

ということで、どうにかしてそういった問題を自動検知できないか、と調べていた時にimportプラグインに定義されている no-restricted-paths を見つけました。今回は、その紹介をしようと思います。

import/no-restricted-pathsの設定

no-restricted-paths の実際の記述例を以下に紹介します。

"import/no-restricted-paths": [
      "warn", // or error
      {
        zones: [
          {
            from: "./src/domain/**/*",
            target: "./src/components/!(organisms)/**/*",
            message:
              "The organisms components can only depend on the domain layer",
          },
          {
            from: "./node_modules/openapi",
            target: "./src/components/**/*",
            message:
              "Components layer can not depend on OpenAPI type definition ",
          },
        ],
      },
    ],

まず1つ目の例について。例えば、Atomic Designを採用しているプロダクトにおいては、しばしばcomponents層においてドメインに依存する箇所を制限させたい、というようなルールを取り入れているかもしれません。そのような場合、上記のように記述することで、 organisms以外のコンポーネントにおいて src/domain/配下からのimportを行った場合にアラートを出すことができます。

そして2つ目の例は、node_modules下にレポジトリの依存パッケージとして存在するOpenAPI上の型定義の参照を、components層で許さないことを表現したものになります。 このように、複数個のルールを定義することが可能であり、チームで取り決めた構成に沿った依存ルールを柔軟に表現できることがわかるのではないでしょうか?

(補足)no-restricted-importsについて

no-restricted-imports は上で紹介したルールと似たような名前ですが、こちらは文字通り指定したモジュールのimportを禁止することが可能です。実際のFEプロジェクトでのユースケースとしては、サイズの大きいライブラリ(例えばlodash)の静的なimportを禁止することや、プロダクトとして非推奨となったライブラリの利用を制限させることなどができそうです。こちらもチーム内で取り決めたルールを表現する手段としては非常に有効だと思います。

"@typescript-eslint/no-restricted-imports": [
      "warn",
      {
        paths: [
          {
            name: "lodash",
           "message": "lodash is deprecated, please use 'just'",
          },
        ],
      },
    ],

最後に

チームで取り決めたルール・運用をESlint上に表現し自動検知できるようにすることで、開発をする上での実装者・レビュワーの工数・負担を軽減させることは間違いありません。弊社プロダクト TuneCore Japan もお陰様でフロントエンド開発に携わってくれるメンバーが少しずつ増えてきており、こういった改善策の恩恵をより受けることができているのではないかな、と思います。

また、TuneCore Japan では 一緒に働くメンバーを募集しています。

エンジニアだけでなく、Director等の各種ポジションをオープンしているので興味がわいた方はぜひご覧ください!

www.tunecore.co.jp