Open5

モノレポ構成で複数のアプリを実装してみる①(Flutter × Melos)- 準備編 -

ohtsukiohtsuki

モノレポとは

https://zenn.dev/burizae/articles/c811cae767965a

モチベ

最近PJでMelosを触ることがあって、(覚えたてなのもあり)技術アウトプットも含めて自分メモ用に書き散らしたくなったのを動機にコード書きながら新規Flutter PJ立ち上げのワークフロー的なものをぼちぼち上げていきたいと思う。

  • どういったケースに導入すると良さそうなのか
    • 実務ではOEM版アプリを制作する時に導入となりました。
  • どういったことがメリットになるのか
  • ついでに好き勝手コード書きたい
ohtsukiohtsuki

Melos is 何

https://melos.invertase.dev/getting-started

導入の説明などは他の方々が良記事上げてくださっているので、スクラップからは除外しちゃう。

使用技術スタック

  • 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ですよってことです。

ohtsukiohtsuki

下準備

  • とりあえずdev / prodでビルドを切り替えれる
  • Firebaseを読み込めるようにだけしとく
  • READMEはおいおい書くね

出来上がりはこちら

https://github.com/sus-ohtsuki/melos-sample/tree/launch_flavor_app

それぞれのアプリを環境ごとにビルドするとこんな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",
    },
  ]
}
ohtsukiohtsuki

パッケージ情報を取得するモジュールをpackages/coreに設置

アプリ1でも2でも、バージョン情報とかアプリ名とか取得しそうなのでpackages/coreへ実装します。

使用パッケージ
https://pub.dev/packages/package_info_plus

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

ブランチ

https://github.com/sus-ohtsuki/melos-sample/tree/feature/1-package-info