はじめに
たとえばフィットネス系のようなアプリケーションの場合、「トレーナーアプリ」「ユーザーアプリ」「管理者アプリ」のように3種類のアプリが必要になる場合があります。
では、その時のLaravelプロジェクトはどう管理すべきでしょうか?
大きく以下の2パターンがあると思います。
- ルーティングで大きく
usrs
,trainers
,admin
のグループ分けをし、Controller系も同様にディレクトリ分けする - 別リポジトリとして切り分ける
前者、後者でそれぞれメリデメはあると思います。
あくまで一例です
同一リポジトリでルートを分ける(モノリス)
メリット
- ひとつのリポジトリで全てを管理できる
- 共通ロジックを管理しやすい
デメリット
- 共通ロジック+ユーザー種別ごとのロジックが増えた場合ファットになりやすい
- 共通化する / しないの線引きを誤るとメンテナンスが困難になりうる
- 設定値もそれぞれswitchする必要があったりと手間がかかる箇所がある
別リポジトリとして切り分ける(マルチレポ)
メリット
- ユーザー種別に特化した実装にすることができる
- ドメインの切り分け、各種設定値の切り分けが容易
デメリット
- 二重の実装が必要なので実装漏れの可能性がある
ここで登場するのがモノレポという概念です。
参考になりそうな記事
ディレクトリ構造のサンプル
今回は、フィットネスアプリがあり、新たにフランチャイズ(FC)展開するためプロジェクトを分ける必要が出てきました。
そこで今回のモノレポ構造を検討してみます。
まずは全体像についてです。
全体像
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の中身についてもう少し展開します
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を以下のように設定します。
{
"name": "app-name/backend",
"autoload": {
"psr-4": {
"AppName\\BackEnd\\": "src/"
}
}
}
あとは各アプリ側の設定をします
composer require app-name/backend:dev-main
Nuxtの場合も同様にpackage.jsonで共有すれば共通ロジック・共通パーツの外出しが可能です。
また、migrationファイルについても読み取り先の変更が必要です。
上記の例で言うとCommonServiceProvider.php
で以下のboot()
メソッドを定義します。
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
以上がLaravelプロジェクトのモノレポ化する一例になります。
いつか続編で上記のハンズオンを投稿しようと思います。
また、モデルを外出しする場合ややモデルに細かい設定が必要になる場合もあります。
モデルでできる設定については先日投稿したこちらも参考にしてみてください。