モノレポ構成で複数のアプリを実装してみる①(Flutter × Melos)- 準備編 -
モノレポとは
モチベ
最近PJでMelosを触ることがあって、(覚えたてなのもあり)技術アウトプットも含めて自分メモ用に書き散らしたくなったのを動機にコード書きながら新規Flutter PJ立ち上げのワークフロー的なものをぼちぼち上げていきたいと思う。
- どういったケースに導入すると良さそうなのか
- 実務ではOEM版アプリを制作する時に導入となりました。
- どういったことがメリットになるのか
- ついでに好き勝手コード書きたい
Melos is 何
導入の説明などは他の方々が良記事上げてくださっているので、スクラップからは除外しちゃう。
使用技術スタック
- Flutter v3.24.3
- Firebase(FirebaseAuth / FCM)
- 必要に応じて
作成するアプリのざっくり要件と構成
- リリースを想定しているアプリが2つ(以上)ある
- 1️⃣2つのアプリでは共通するAPIをクライエント側で呼び出す仕様である
- 2️⃣認証やPUSH通知モジュールなど、2つのアプリで共通使用しコストを抑えたい(アプリごとの個別実装をしなくて済む)
- 3️⃣2つのアプリは構成する部品などのデザインは同じものを使用するが、色やテーマカラーなどで違いを表現したい
以下の図はMelosのデフォルト構成に、今回の要件に必要なpackage群を追記したものになります。
2つのアプリはapps内にそれぞれ設置し、共通で使用を想定しているものはpackages内に設置する方針です。
my_project
├── apps
│ ├── app1
│ └── app2
├── packages
│ ├── 1️⃣api_client
│ ├── 2️⃣core
│ └── 3️⃣widget
├── melos.yaml
├── pubspec.yaml
└── README.md
従来のポリレポ構成ではなし得なかった、別々に独立したアプリ内でのコードの共有ができることが最大のメリットだと実感。例えばアプリ1と2で使用しているAPIの仕様が変更になった場合、packages内のコードを1箇所変更するだけで、アプリ1と2に変更を同時に適用できるってわけです。
つまりエンジニアは楽ができるし、ユーザに価値を早く提供できるし、コストも抑えられてビジネス的にもWin-WIn-Winですよってことです。
下準備
- とりあえずdev / prodでビルドを切り替えれる
- Firebaseを読み込めるようにだけしとく
- READMEはおいおい書くね
出来上がりはこちら
それぞれのアプリを環境ごとにビルドするとこんなHOME画面になります。
- アプリ1をMEM
- アプリ2をMEM 2
開発環境の方にdev.とsuffixを表示してアイコンを変えてます。(ちなみにAndroid用の素材用意してないので、アイコンちっちゃいです。すまん。)
ポイント
vscodeのlaunch.jsonはこんな感じ。releaseモードでビルドできるようによしなに設定が必要ですが(気が向いたらやる)、アプリが2つあるのでcwd
で起動したいアプリのディレクトリを指定して、program
でmain.dartを指定します。
{
"version": "0.2.0",
"configurations": [
{
"name": "app1/dev",
"request": "launch",
"type": "dart",
"cwd": "apps/membership_app1",
"program": "lib/main.dart",
"args": ["--dart-define-from-file=dart_defines/dev.env"],
"flutterMode": "debug",
},
{
"name": "app1/prod",
"request": "launch",
"type": "dart",
"cwd": "apps/membership_app1",
"program": "lib/main.dart",
"args": ["--dart-define-from-file=dart_defines/prod.env"],
"flutterMode": "debug",
},
{
"name": "app2/dev",
"request": "launch",
"type": "dart",
"cwd": "apps/membership_app2",
"program": "lib/main.dart",
"args": ["--dart-define-from-file=dart_defines/dev.env"],
"flutterMode": "debug",
},
{
"name": "app2/prod",
"request": "launch",
"type": "dart",
"cwd": "apps/membership_app2",
"program": "lib/main.dart",
"args": ["--dart-define-from-file=dart_defines/prod.env"],
"flutterMode": "debug",
},
]
}
パッケージ情報を取得するモジュールをpackages/coreに設置
アプリ1でも2でも、バージョン情報とかアプリ名とか取得しそうなのでpackages/coreへ実装します。
使用パッケージ
coreの構成はこんな感じ
packages/core
├── lib
│ ├── data
│ │ └── package_info_manager.dart
│ ├── data.dart
│ ├── model
│ │ └── app_package.dart
│ ├── model.dart
│ ├── repository
│ │ └── package_info_repository.dart
│ └── repository.dart
├── pubspec.lock
├── pubspec.yaml
└── test
dev / prodでアプリ名が切り替わるか
Dev App1 | Prod App1 |
---|---|
ブランチ
melos.yamlに色々追加
- build_runner
- linter
- test
- その他
全部のパッケージを対象にすることももちろん、個別のパッケージのみスコープを絞ってコマンドの実行ができます。