# プロジェクトアーキテクチャ
この章では、フロントエンドのプロジェクトのアーキテクチャについて見ていきたいと思います。一般的にプロジェクトのアーキテクチャは、モノリス、マイクロサービス、モジュラーモノリスなどがあります。バックエンドではマイクロサービスが一般的になりましたが、フロントエンドでもマイクロフロントエンドというアーキテクチャが採用されつつあります。アーキテクチャでは、フロントエンドで一般的なモノリスの構成とマイクロフロントエンドについて見ていきたいと思います。 また、リポジトリの構成では、ポリレポとモノレポの違い、モノレポの実装方法、CIの実装などを検証したいと思います。
# アーキテクチャ
# モノリス
モノリス(モノリシックアーキテクチャ)は、単一の独立したアーキテクチャを指します。1つのアプリケーションに対して単一のコードベースで管理し、チーム全体で同じリポジトリを開発します。また、リリースも単一のデプロイメントで管理します。APIへの接続は、複数または、BFFのように単一のパターンがありますが、フロントエンドの実装部分は単体のコードベースで管理します。
# メリット
モノリスは、単一のコードベースで管理するため、認知コストを下げることができます。一つのリポジトリにコンポーネントやAPI接続の実装が全て収まっているため、開発の初期段階では、設計しやすいというメリットがあります。また、デプロイも単一になるため、CI/CDの構成がシンプルになります。チーム開発においても、同じコードを共有するので、追加機能やリファクタリングのレビューがしやすいというメリットがあります。そのため、小規模から中規模なアプリケーションで、スピード重視の開発の場合は、モノリスが有効なアーキテクチャになるでしょう。
# デメリット
モノリスのデメリットは、コードベースが肥大化してしまうことです。開発メンバーが増え、機能が増えていくことでコードベースが段々と大きくなっていきます。コードが肥大することで、シンプルだった構成が複雑になり、様々な問題が発生する可能性があります。同じようなコンポーネントや処理が増えたり、コードが複雑化して不具合の特定が難しくなったり、テストが遅い、依存関係が原因でライブラリのバージョンアップができなかったり、などのケースが発生します。
また、少しの修正でもアプリケーション全体のデプロイをする必要があるため、リリースまでのリードタイムが増加する可能性があります。 モノリスは、小規模から中規模なアプリケーションで有効ですが、大規模なアプリケーションではデメリットの方が大きくなる傾向があります。チームのガイドラインを定めることである程度回避をすることはできますが、大規模になってくると難しい場合もあるでしょう。
# モノリスの実装
では、フロントエンドのモノリスの例を見てみましょう。
次のようなNext.jsで構成されたDashboardアプリケーションを想定してみます。
ページは、Dashboard
、Buttons
、Forms
、Alerts
、Ratings
、Images
、Pagination
、Tables
とあり、一つのリポジトリで管理します。プロジェクト構成は次の通りです。
dashboard-app
├── package.json
├── pages
│ ├── _app.js
│ ├── _document.js
│ ├── alerts.js
│ ├── buttons.js
│ ├── forms.js
│ ├── image.js
│ ├── index.js
│ ├── pagination.js
│ ├── rating.js
│ └── table.js
├── public
│ ├── favicon.ico
│ ├── static
│ └── vercel.svg
├── src
│ ├── components
│ ├── pages
│ ├── shared
│ ├── styles
│ └── theme
└── yarn.lock
プロジェクト構成は、Next.jsのテンプレートで展開されています。ページのルーティングは、pages
に定義し、実装コードはsrc
に展開しています。APIエンドポイントを追加する場合は、api
フォルダに作成することができます。
モノリスの場合、パッケージや、共有モジュール、コンポーネントなど全てのコードを一つのフォルダに格納して管理します。これぐらいのページ数だと見通しが良く、管理しやすいと思います。しかし、もし数十ページと増えていくと、扱うコンポーネントも大量になるためメンテナンスが難しくなるでしょう。サービスやチームが大きくなるにつれて、プロジェクト構成も再検討する必要があります。
# マイクロフロントエンド
マイクロフロントエンドは、アプリケーションを細分化し、チームごとに独立させて開発するためのアーキテクチャです。 モノリスでは、コードベースやチームが大きくなるにつれて、メンテナンスコストが増加するというデメリットがありました。機能やページが増えるごとにシステムの複雑性が増し、リリースサイクルが遅れる可能性が発生します。
マイクロフロントエンドは、このようなモノリスの課題を解決するために登場しました。マイクロサービスの考え方をフロントエンドにも拡張し、単一のチームにバックエンド、フロントエンド、デザイナーが一緒になって独立した開発を実現すること目指します。
マイクロフロントエンドの最大のメリットは、チームを細分化することで、柔軟な開発体制を実現することです。機能ごと、あるいはページごとにチームを分割することで特定のドメインを持ったチームで開発することができます。そのため、設計コストや運用コストを抑えることができ、開発効率を上げることができます。各サービスが扱う技術やフレームワークもそれぞれが選ぶことができ、デプロイも独立して実行可能となります。
マイクロフロントエンドのリポジトリ構成は、ポリレポかモノレポを選択することができます。ポリレポの場合は、各サービスのリポジトリを独立して開発します。モノレポの場合は、一つのリポジトリでサービスを分割して開発します。それぞれメリット、デメリットがありますが、モノリスから移行する場合は、同じバックエンドAPIを持つことが多いので、モノレポの方が効率的な場合があります。例えば、バックエンドAPIの接続情報を共通化したり、LintやTypeScriptの設定を共有することができます。
モノリスで参考にしたアプリケーションを例に見てみましょう。
もし、ページごとにチームを分割する場合、Dashboard
、Buttons
、Forms
、Alerts
、Ratings
、Images
、Pagination
、Tables
と分けることができます。
その場合、モノレポで作ると、次のようなプロジェクト構成で展開できます。
├── apps
│ ├── alerts
│ ├── buttons
│ ├── dashboard
│ ├── forms
│ ├── images
│ ├── pagination
│ ├── ratings
│ └── tables
├── package.json
├── pakcages
│ ├── components
│ ├── eslint
│ └── tsconfig
└── yarn.lock
apps
以下は、それぞれのサービスを定義します。各サービスは独立して開発できるため、異なるフレームワークやライブラリを使用することができます。また、デプロイも単独で実行可能です。packages
には、共通のLintやTypeScriptの設定ファイルを定義します。また、横断的に使用する共通コンポーネントもここに配置します。
マイクロフロントエンドの場合、共通のコンポーネントやモジュール、設定ファイルが多くなる傾向があります。モノレポで構成する場合は、そのようなニーズを満たすことが可能となっています。だたし、分割する粒度やチームによって要件は異なるため、よく検討して導入する必要はあります。ポリレポとモノレポの詳細は、次のリポジトリ構成で詳しく見たいと思います。
マイクロフロントエンドの詳しい説明や実装方法は、アーキテクチャのMicro Frontendsの章をご確認ください。
# リポジトリ構成
# ポリレポ(Polyrepo)
ポリレポとは、マイクロサービスで、サービスごとにリポジトリを管理する手法です。バックエンドのマイクロサービスでは、一般的なリポジトリ構成になっています。バックエンドのマイクロサービスでは、異なる言語やフレームワークを選定する可能性があるため、リポジトリを分けていた方が運用しやすい場合があります。
フロントエンドにおいてもポリレポの構成は可能です。例えば、複数のサービスを展開するプロジェクトの場合、サービスごとにリポジトリを分けることができます。コンシューマー向けのアプリと、企業向けのCMSなど、別のリポジトリで管理するケースが当てはまるでしょう。 また、マイクロフロントエンドでも、各チームごとでリポジトリを分けるパターンも考えられます。
ポリレポは、リポジトリを完全に分けるため、チームの独立性を優先する場合に有効的です。共通のコンポーネントは、別のリポジトリで管理してNPMとして配信するなどのケースもあるでしょう。しかし、共通の処理が多かったり、同じバックエンドAPIを参照していたり、パッケージの管理を統一したいなどの場合はモノレポの方が効率的な場合があります。
このあたりはサービスの規模や要件によって異なるため、チームで検討する必要があるでしょう。
# モノレポ(Monorepo)
モノレポとは、複数のサービスを一つのリポジトリで管理する手法です。GoogleやMicrosoft、Stripeなどの企業で採用されているリポジトリ構成です。ポリレポがサービスごとにリポジトリを用意するのに対して、モノレポは全てのサービスを一つのリポジトリに集約し、モジュール化して管理します。一箇所で全てを管理する点ではモノリスと似ているかもしれませんが、モノレポは以下のような特徴を持っています。
- モジュールの依存関係を適切に管理できる
- サービスごとにデプロイできる
- キャッシュでビルドの効率化ができる
- 変更箇所の影響範囲を明確にできる
モノリスの場合、共通化したモジュールをどこでも使用することができるため、依存関係が複雑化しやすい傾向にあります。また、デプロイするたびにアプリケーション全体のビルドが必要になるため、リリースサイクルが遅くなる可能性があります。 しかし、モノレポでは、適切にモジュールの依存関係を整理し、共有することで、プロジェクト全体の効率化を実現することができます。
また、ポリレポではリポジトリを分けることで独立性を保てる反面、共通の設定ファイルがバラバラに管理されたり、新規サービスを一から立ち上げるコストなどの課題がありますが、モノレポは、共通の設定やコンポーネント、デプロイメントを共有できるため、サービスの立ち上げやしやすいというメリットがあります。
実際に、どのようにファイルが共有されるか見てみましょう。 一般的に、モノレポの構成は次のようになっています。
project-root
├── apps
│ ├── web
│ ├── api
│ └── cms
├── package.json
├── pakcages
│ ├── components
│ ├── eslint
│ └── tsconfig
└── yarn.lock
ルートのpackage.json
で共通のライブラリを管理します。apps
には各サービスを展開し、packages
はサービスで共有するための共通ファイルを配置します。例えば、apps/web
でpackages/tsconfig
を使いたい場合は、packages.json
に次のように書くことでインポートして使うことができます。
{
"name": "@apps/web",
"version": "1.0.0",
"devDependencies": {
"@packages/tsconfig": "*"
}
}
NPMのパッケージをインストールするように、packages
内のモジュールを使うことができます。サービスがNext.jsやAstroなど異なるフレームワークを使っていても、このようにインポートすることで共通の設定をプロジェクトで共有することができます。NPMパッケージとして公開すれば同じような構成をすることは可能ですが、変更を加える度にNPMの更新をしなければなりません。モノレポの方が、すぐに変更できるという点で、スピード感をもって開発することができます。
モノレポの実装は、npmやyarnのworkspaces (opens new window)機能を使いますが、それ以外にも次のような管理ツールがあります。
- bazel (opens new window)
- Gradle (opens new window)
- Lage (opens new window)
- Lerna (opens new window)
- Nx (opens new window)
- Pants (opens new window)
- Rush (opens new window)
- Turborepo (opens new window)
フロントエンド開発だと、Lerna、Nx、Turborepoを使うことが多いでしょう。この章では、Turborepoを例にモノレポの実装を見たいと思います。
# メリット
フロントエンド開発でのモノレポのメリットは、次のようなものがあります。
- シンプルにできる
- ライブラリの管理がしやすい
- ESLintやTypescriptの設定を共有できる
- 共通のモジュールを横断的に使える
- テストの共通化ができる
- フロントエンドとバックエンドのスキーマを共有できる
- 新規サービスの立ち上げがしやすい
モノリスでは見通しが悪くなっていたコードを細かくモジュールに分けることで、シンプルな構成にすることができます。小さくする分、依存性が整理されてメンテナンス性が向上します。
モノレポはプロジェクトルートのpackage.json
で依存関係の管理ができるため、共通で使用しているライブラリのバージョンアップがしやすくなります。また、ESLintやTypeScriptの設定ファイルを共有することで、開発の効率化を実現できます。
フロントエンドとバックエンドでTypeScriptを使っている場合、スキーマの型ファイルを共有することができます。ポリレポの構成でフロントとバックエンドが別の言語の場合、GraphQL Code Generator (opens new window)やOpen APIの自動生成、gRPCなどの仕組みを活用すれば同じことが実現可能ですが、言語を統一した方が実装コストを抑えて開発することができるでしょう。
プロジェクトによっては、複数のサービスを同時並行で進めることもあると思います。そのような場合、ポリレポでは一つ一つのサービスを立ち上げてセットアップする必要があります。ライブラリをインストールし、ESLintやTypeScript、テストの環境構築が必要になります。しかし、モノレポでは、すでに共有ファイルがあるのでそれらをインポートして使えば、同じ構成で新規サービスを始めることができます。そのため、プロジェクトの効率化とスピードを優先したい場合、モノレポの方が有効的になるでしょう。
# デメリット
次に、モノレポのデメリットを見てみましょう。
- リポジトリが肥大化する
- ビルドに時間がかかる
- 複数の言語が混在すると、見通しが悪くなる
モノレポは、一つのリポジトリに全てのサービスやモジュールを管理するため、自然とコードベースが肥大化していきます。その分、関わるドメインも増えるため、IssueやPRの粒度、CIの調整が必要になります。
また、共通処理のビジネスロジックなどコアの部分が大きくなってくると、ビルド時間に影響を及ぼします。TurborepoやNxなどはキャッシュの活用でビルドの効率化をしていますが、全てのサービスで依存されているコードの場合、キャッシュが効かず遅くなる可能性があります。
また、なんとなくモノレポを導入して、関係のないドメインや複数の言語が入り混じるような状況になった場合、プロジェクト全体の見通しが悪くなる可能性があります。 モノレポで重要なことは、コードを適切に分けてシンプルに保つことです。コードをシンプルにすることで、依存関係を明確にし、影響範囲を限定的にコントロールできるようになります。共通化する必要のないものまでモジュール化しても、逆にメンテナンス性が下がる可能性があります。アプリケーションが複雑化し、メンテナンスするのが難しくなって初めてモノレポ化の検討をするのがいいでしょう。
# モノレポの実装
それでは、Turborepoを用いたモノレポの実装を見てみましょう。
Turborepoは、Vercelが開発したモノレポのビルドツールです。npm、yarn、pnpmのworkspacesをベースにモノレポを構築することができます。Turborepoは、モジュールの依存性の解決を自動的に行うため、実装者は依存関係の順番を意識することなくコードを書くことができます。また、各タスクのキャッシュ (opens new window)をすることで、ビルドやテスト、Linterなどの処理を効率化に実行することができます。
Turborepoでは、プロジェクト生成のCLIが用意されているためそれを使用します。
$ npx create-turbo@latest
生成されたファイルは次のような構成になっています。
apps
- apps/docs
- apps/web
packages
- packages/eslint-config-custom
- packages/tsconfig
- packages/ui
apps
がサービスで、packages
が共通で利用するモジュールになっています。apps/web
のpackage.json
を見てみましょう。以下のようにui
モジュールがインストールされてます。
{
"dependencies": {
"next": "^13.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ui": "*"
}
apps/web/app/page.tsx
では、このui
モジュールのコンポーネントをインポートして使うことができます
import { Button, Header } from "ui";
export default function Page() {
return (
<>
<Header text="Web" />
<Button />
</>
);
}
ui
モジュールのButton
コンポーネントは以下のようなtsx
ファイルです。
"use client";
import * as React from "react";
export const Button = () => {
return <button onClick={() => alert("boop")}>Boop</button>;
};
apps/web
からは直接このButton.tsx
ファイルをインポートしてビルドしています。通常、WebpackやTurbopackなどのバンドラツールはnode_modules
やローカルパッケージをビルド対象としていないので、そのままtsxファイルを読み込むとエラーが発生してしまいます。
../../packages/ui/Button.tsx
Module parse failed: Unexpected token (3:9)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import * as React from "react";
| export const Button = () => {
> return <button>Boop</button>;
| };
|
これを回避すべく、Next.js13では、transpilePackages (opens new window)というオプションが用意されており、ここにパッケージを指定することで、ビルドを実行することができます。今回のようにui
モジュールをビルド対象とする場合は、next.config.js
で以下のように指定します。
module.exports = {
transpilePackages: ["ui"],
};
サービス側でビルドを行わない場合は、ui
モジュールでビルドを実行してjs
ファイルを配信する必要があります。今回の例だと、ui/package.json
にmain
をindex.js
に書き換えて、dev
スクリプトを追加します。
{
"name": "ui",
"version": "0.0.0",
"main": "./index.js",
"types": "./index.tsx",
"license": "MIT",
"scripts": {
"lint": "TIMING=1 eslint \"**/*.ts*\"",
"dev": "tsc --watch"
},
"devDependencies": {
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"eslint": "^7.32.0",
"eslint-config-custom": "*",
"react": "^18.2.0",
"tsconfig": "*",
"typescript": "^4.5.2"
}
}
これで、turbo run dev
することでworkspaces内にあるdev
スクリプトが実行され、ui
モジュールのビルドも実行されます。yarn dev
すると次のように並列で実行されているのが分かります。
yarn dev
...
docs:dev: ready - started server on 0.0.0.0:3001, url: http://localhost:3001
web:dev: ready - started server on 0.0.0.0:3000, url: http://localhost:3000
ui:dev:
ui:dev: 10:27:16 - Starting compilation in watch mode...
このように事前にビルドしておくことで、サービス側は生成されたJavaScriptファイルをそのまま使用することができます。
続いて、スクリプトの依存関係やキャッシュの管理は、turbo.json
で設定します。
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"test": {
"dependsOn": ["lint", "build"]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true
}
}
}
Pipeline (opens new window)を設定することで、各スクリプトに対して依存関係のあるタスクを設定したり、キャッシュするアウトプット先を指定できます。
npmやyarnのworkspaces単体だと、ビルドや依存関係の解決などを自前で用意する必要がありますが、Turborepoではビルドの最適化やHMR、依存関係の解決などの機能を使うことができるので、効率的にモノレポの開発をすることができます。また、Remote Caching (opens new window)という機能もあり、クラウドサーバ上にキャッシュを保持し、複数の端末やCI環境でキャッシュを共有することができます。
出典: Turborepo (opens new window)
# CIの実装
次に、モノレポにおけるCIの実装を見てみましょう。モノレポでは、フロントエンドのサービスやバックエンドのサービスが混在することがあります。その場合、変更がある度に全てのサービスをCI上で検証すると非効率になります。そのため、モノレポ環境では、変更されたファイルに応じてタスクを割り振る必要があります。この章では、GitHub ActionsとCircleCIでのモノレポの設定方法を見てみましょう。
# GitHub Actions
GitHub Actionsでは、paths
オプションを使うことで特定のファイルをフィルタリングすることができます。例えば、apps/web
サービスの変更があったときだけビルドを実行したい場合は次のように書くことができます。
name: Web
on:
pull_request:
paths:
- 'apps/web/**'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: 16
cache: yarn
- name: Cache node_modules
uses: actions/cache@v2
id: node_modules_cache_id
with:
path: node_modules
key: v1-yarn-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
restore-keys: |
v1-yarn-
- name: Install dependencies
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --silent
- name: Lint
run: yarn lint --filter=web
- name: Build
run: yarn build --filter=web
Turborepoでは、--filter
オプションを使うことで、特定のタスクを実行することができます。今回のケースは、apps/web
のビルドだけ実行したいので--filter=web
と指定します。
- name: Lint
run: yarn lint --filter=web
- name: Build
run: yarn build --filter=web
同様に、apps/docs
だけのworkflowを実行する場合は次のよう書くことができます。
name: Docs
on:
pull_request:
paths:
- 'apps/docs/**'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: 16
cache: yarn
- name: Cache node_modules
uses: actions/cache@v2
id: node_modules_cache_id
with:
path: node_modules
key: v1-yarn-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
restore-keys: |
v1-yarn-
- name: Install dependencies
if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --silent
- name: Lint
run: yarn lint --filter=docs
- name: Build
run: yarn build --filter=docs
それぞれのworkflowは、ファイルを分割して管理できるのでフロントエンド、バックエンド、モジュールごとのようにworkflowを設定することができます。
.github/workflows/
├── apps/frontend/docs.yml
├── apps/frontend/web.yml
├── apps/backend/api.yml
└── packages/ui.yml
# CircleCI
CircleCIで分割したタスクを実行するためには、ダイナミックコンフィグ (opens new window)とcircleci/path-filtering (opens new window)という機能を使います。ダイナミックコンフィグ
は、CI実行時に動的にconfigを生成する機能で、circleci/path-filtering
は変更されたファイルに応じて特定のworkflowを実行するプラグインになります。
今回は、以下のようにサービスごとにconfigを管理し、変更があったコードに応じてサービスのbuildを実行したいと思います。
.circleci/
├── config.yml
└── workflows
├── docs.yml
└── web.yml
複数のconfigをマージする機能は、circleci config pack
というCLIで実現可能ですが、ディレクトリ構成に制約があるため見送ります。より柔軟にマージできるように、yq (opens new window)というYAMLのマージツールを使用します。yqでworkflows
以下のconfigをマージし、その設定ファイルをもとにCIを実行します。
まとめると全体的なフローは次の通りです。
- yqで
workflows
のconfigをマージする - path-filtering/filterで変更のあったファイルをフィルタリングし、特定のworkflowを実行
TIP
CircleCIのダイナミックコンフィグを使うためには、.circleci/config.yml
にsetup: true
を設定するのとProject Settingsで設定をONにする必要があります。
より詳しい情報は、CircleCI のダイナミックコンフィグの入門ガイド (opens new window)をご確認ください。
まずは、メインとなる.circleci/config.yml
を作成しましょう。
version: 2.1
setup: true
orbs:
path-filtering: circleci/path-filtering@0.1.5
jobs:
setup:
docker:
- image: cimg/go:1.20.4
steps:
- checkout
- run:
name: Install yq
command: |
go install github.com/mikefarah/yq/v4@latest
yq --version
- run:
name: Merge config files
command: |
mkdir -p /tmp/
yq eval-all '. as $item ireduce ({}; . * $item )' ./.circleci/workflows/*.yml > /tmp/merged.yml
cat /tmp/merged.yml
- persist_to_workspace:
root: /tmp
paths:
- merged.yml
workflows:
config:
jobs:
- setup
- path-filtering/filter:
requires:
- setup
workspace_path: .
base-revision: main
pre-steps:
- attach_workspace:
at: /tmp
config-path: /tmp/merged.yml
mapping: |
apps/web/.* build-web true
apps/docs/.* build-docs true
setup
では、Goのinstallでygをインストールし、workflows
のconfigをマージしています。実際にマージされたファイルは次の通りです。
version: 2.1
orbs:
node: circleci/node@5.1.0
parameters:
build-docs:
type: boolean
default: false
build-web:
type: boolean
default: false
jobs:
docs-build:
executor: node/default
steps:
- checkout
- node/install-packages:
pkg-manager: yarn
- run:
command: yarn lint --filter=docs
name: Run lint
- run:
command: yarn build --filter=docs
name: Build app
web-build:
executor: node/default
steps:
- checkout
- node/install-packages:
pkg-manager: yarn
- run:
command: yarn lint --filter=web
name: Run lint
- run:
command: yarn build --filter=web
name: Build app
workflows:
docs-build:
when: << pipeline.parameters.build-docs >>
jobs:
- docs-build
web-build:
when: << pipeline.parameters.build-web >>
jobs:
- web-build
parameters
に注目すると、build-docs
とbuild-web
が設定されています。workflows
では、このパラメータがあるときだけ以下のようにworkflowを実行するようになっています。
workflows:
docs-build:
when: << pipeline.parameters.build-docs >>
jobs:
- docs-build
web-build:
when: << pipeline.parameters.build-web >>
jobs:
- web-build
path-filtering/filter
で、このマージされたconfigを指定します。mapping
では、変更されたファイルに応じてパラメータを設定します。
mapping: |
apps/web/.* build-web true
apps/docs/.* build-docs true
これは、apps/web/
以下のファイルに変更があった場合にbuild-web
というパラメータにtrue
を設定するという意味です。先ほどのマージされたconfigをもう一度見ると、build-web
パラメータがあるときはweb-build
を実行するように設定しているのが分かります。
workflows:
web-build:
when: << pipeline.parameters.build-web >>
jobs:
- web-build
これにより、web-build
ジョブでビルドが実行されるようになります。apps/docs
に関しても同様です。例えば、apps/web/app/page.tsx
を変更すると、次のようにworkflowが実行されます。
マージ前の.circleci/workflows/web.yml
も一応見ておきましょう。
version: 2.1
orbs:
node: circleci/node@5.1.0
parameters:
build-web:
type: boolean
default: false
jobs:
web-build:
executor: node/default
steps:
- checkout
- node/install-packages:
pkg-manager: yarn
- run:
command: yarn lint --filter=web
name: Run lint
- run:
command: yarn build --filter=web
name: Build app
workflows:
web-build:
when: << pipeline.parameters.build-web >>
jobs:
- web-build
apps/web
だけのworkflowとjobを設定をすればいいので、見通しがよくなっているのが分かります。 このようにCircleCIではダイナミックコンフィグとcircleci/path-filteringを使うことで、モノレポのCIの実装が可能となっています。
← フロントエンドの設計 コンポーネント →