Laravelでモノレポ開発をしたい

## はじめに

たとえばフィットネス系のようなアプリケーションの場合、「トレーナーアプリ」「ユーザーアプリ」「管理者アプリ」のように3種類のアプリが必要になる場合があります。

では、その時のLaravelプロジェクトはどう管理すべきでしょうか？
大きく以下の2パターンがあると思います。

- ルーティングで大きく`usrs`, `trainers`, `admin`のグループ分けをし、Controller系も同様にディレクトリ分けする
- 別リポジトリとして切り分ける

前者、後者でそれぞれメリデメはあると思います。

:::note warn
あくまで一例です
:::

## 同一リポジトリでルートを分ける（モノリス）
### メリット
- ひとつのリポジトリで全てを管理できる
- 共通ロジックを管理しやすい

### デメリット
- 共通ロジック＋ユーザー種別ごとのロジックが増えた場合ファットになりやすい
- 共通化する / しないの線引きを誤るとメンテナンスが困難になりうる
- 設定値もそれぞれswitchする必要があったりと手間がかかる箇所がある

## 別リポジトリとして切り分ける（マルチレポ）
### メリット
- ユーザー種別に特化した実装にすることができる
- ドメインの切り分け、各種設定値の切り分けが容易

### デメリット
- 二重の実装が必要なので実装漏れの可能性がある

ここで登場するのがモノレポという概念です。


## 参考になりそうな記事

https://zenn.dev/akkino/articles/88256598f10a9b

https://logmi.jp/main/technology/321292


## ディレクトリ構造のサンプル

今回は、フィットネスアプリがあり、新たにフランチャイズ（FC）展開するためプロジェクトを分ける必要が出てきました。
そこで今回のモノレポ構造を検討してみます。

まずは全体像についてです。


### 全体像

```md
monorepo-root/
├─ infra/
│  ├─ docker/
│  │  ├─ fitness-app/       # 従来のFitness App用Docker環境
│  │  └─ fitness-app-fc/    # FC用Fitness AppのDocker環境
│  ├─ scripts/
│  │  ├─ build.sh
│  │  ├─ deploy.sh
│  │  └─ ... 
│  └─ README.md
│
├─ packages/
│  ├─ core/
│  │  ├─ backend/     # Laravel用共通サービス・Provider、Eloquentモデルなど
│  │  ├─ domain-logic/       # ドメインモデルや共通ドメインロジック
│  │  └─ frontend-lib/       # Nuxt側で共通利用するライブラリ・utilsなど
│  │
│  ├─ backend/
│  │  ├─ fitness-app/        # 従来のLaravelコード
│  │  └─ fitness-app-fc/     # FC版Laravelコード
│  │
│  └─ frontend/
│     ├─ fitness-app/        # 従来のNuxtプロジェクト
│     └─ fitness-app-fc/     # FC版Nuxtプロジェクト
│
└─ README.md
```

次に、coreの中身についてもう少し展開します

```md
core/
├─ backend/
│  ├─ composer.json
│  ├─ src/
│  │  ├─ Models/
│  │  │  ├─ User.php
│  │  │  └─ Trainer.php
│  │  │
│  │  ├─ Providers/
│  │  │  └─ CommonServiceProvider.php
│  │  │
│  │  ├─ Services/
│  │  │  ├─ PaymentService.php
│  │  │  └─ NotificationService.php
│  │  │
│  │  ├─ Repositories/
│  │  │  ├─ UserRepository.php
│  │  │  └─ WorkoutRepository.php
│  │  │
│  │  ├─ Traits/
│  │  │  └─ Sluggable.php
│  │  │
│  │  └─ Helpers/
│  │     └─ StringHelper.php
│  │
│  ├─ database/
│  │  ├─ migrations/
│  │  │  ├─ 2024_01_01_000000_create_users_table.php
│  │  │  ├─ 2024_01_01_000001_create_trainers_table.php
│  │  │  └─ 2024_01_01_000002_create_workouts_table.php
│  │  │
│  │  ├─ seeders/
│  │  │  └─ UsersTableSeeder.php
│  │  │
│  │  └─ factories/
│  │     ├─ UserFactory.php
│  │     └─ TrainerFactory.php
│  │
│  ├─ tests/
│  │  ├─ Models/
│  │  │  └─ UserTest.php
│  │  └─ Services/
│  │     └─ PaymentServiceTest.php
│  │
│  └─ README.md
│
├─ domain-logic/
│  ├─ composer.json
│  ├─ src/
│  │  ├─ Domain/
│  │  │  ├─ Entities/
│  │  │  │  ├─ WorkoutSession.php
│  │  │  │  ├─ Exercise.php
│  │  │  │  └─ Membership.php
│  │  │  ├─ ValueObjects/
│  │  │  │  └─ EmailAddress.php
│  │  │  ├─ Services/
│  │  │  │  ├─ ScheduleService.php
│  │  │  │  └─ DiscountPolicy.php
│  │  │  └─ Policies/
│  │  │     └─ RefundPolicy.php
│  │  │
│  │  └─ Utils/
│  │     └─ Calculator.php
│  │
│  ├─ tests/
│  │  └─ Domain/
│  │     ├─ WorkoutSessionTest.php
│  │     └─ ScheduleServiceTest.php
│  │
│  └─ README.md
│
└─ frontend-lib/
   ├─ package.json
   ├─ tsconfig.json
   ├─ src/
   │  ├─ utils/
   │  │  ├─ apiClient.js
   │  │  ├─ formatDate.js
   │  │  └─ validators.js
   │  │
   │  ├─ components/
   │  │  ├─ CommonButton.vue
   │  │  └─ Loader.vue
   │  │
   │  ├─ services/
   │  │  └─ AuthService.js
   │  │
   │  └─ constants/
   │     └─ endpoints.js
   │
   ├─ tests/
   │  └─ utils/
   │     └─ apiClient.test.js
   │
   └─ README.md
```


## ロジックの共有方法

ディレクトリ通りにはなりますが、各ディレクトリはpackageとして扱います。
Laravelの場合、Composerパッケージとして設定してあげます。
`core/backend`のcomposer.jsonを以下のように設定します。

```json
{
  "name": "app-name/backend",
  "autoload": {
    "psr-4": {
      "AppName\\BackEnd\\": "src/"
    }
  }
}
```

あとは各アプリ側の設定をします

```bash
composer require app-name/backend:dev-main
```

Nuxtの場合も同様にpackage.jsonで共有すれば共通ロジック・共通パーツの外出しが可能です。

また、migrationファイルについても読み取り先の変更が必要です。
上記の例で言うと`CommonServiceProvider.php`で以下の`boot()`メソッドを定義します。

```php
public function boot()
{
    $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
```

以上がLaravelプロジェクトのモノレポ化する一例になります。

いつか続編で上記のハンズオンを投稿しようと思います。

また、モデルを外出しする場合ややモデルに細かい設定が必要になる場合もあります。
モデルでできる設定については先日投稿したこちらも参考にしてみてください。

https://qiita.com/kurogoma939/items/d84e87199e444fe0cc1b