モノレポ構成とは
例えばフィットネス系のトレーナーアプリ・ユーザーアプリを同時に開発する場合、どのようにアプリを管理しますか?また、どのようにアプリを配布しますか?
- 1つのアプリとして、トレーナーログイン・ユーザーログインのページを用意し、とルートで切り分けていく
- リポジトリを完全に分けて別物として扱う
しかし、上記の方法は以下のようなメリデメを含むと思います。
1つのアプリとして、トレーナーログイン・ユーザーログインのページを用意し、とルートで切り分けていく
メリット | デメリット |
---|---|
1つのリポジトリで一括管理ができる ロジックを共通化させやすい |
リポジトリ内のメインディレクトリが 肥大化していく(Flutterで言うlib) |
リポジトリを完全に分けて別物として扱う
メリット | デメリット |
---|---|
固有のロジックの切り分けが容易である。 全く違うデザインにしても問題ない |
共通ロジック・設定があった場合二重で実装することになる |
そのため、特にデメリットの方が負担に感じるような場合、モノレポ構造のアプリ開発を推奨します。
モノレポ構造って何?と言う方はAltive社のflutter_app_templateが参考になります。
リポジトリの一番上の階層にpackages
ディレクトリを容易し、その中に以下のようにそれぞれ管理したいグループ単位で配置します。
例えばflutter_app
からtheme
のものを呼び出したい場合、直接ディレクトリを参照するのではなくpackageとして取り込んで利用します。
このモノレポ構成は以下のメリデメを含みます
メリット | デメリット |
---|---|
各パッケージの責務が明確になる。 共通要素の実装がスムーズになる |
各種モノレポ用の設定をしなくてはならない |
多言語化対応の話
アプリ開発の際にはTextクラスにセットする文字列をハードコーディングしない目的や、複数言語に対応したアプリケーションとする場合localizationパッケージを用いると思います。
多言語化パッケージの紹介
多言語化パッケージはいくつかありますが、代表して以下のようなものがあります
Flutter Gems
代表パッケージ
slangの紹介
今回はその中でSlangの紹介をします。
基本的な使い方は以下の通りです。
packageのインストール
build.yamlの実装
targets:
$default:
builders:
slang_build_runner:
options:
base_locale: ja
input_directory: lib/i18n # 多言語ファイルを設置する場所
input_file_pattern: .yaml # フォーマット形式
output_directory: lib/gen # 自動生成後の出力ディレクトリ
timestamp: false # 自動生成の度にファイルに記録される
statistics: false
yamlの記載
app:
title: "APP NAME"
アプリへの設定
MyApp()
を以下のようにラップしてください
TranslationProvider(
child: const MyApp()
),
使い方
自動生成のため、build_runnerコマンドを走らせると以下のように利用できます。
context.t.app.title // APP NAME
モノレポ特有の複数パッケージでまたがる場合の問題
ここでタイトルの問題ですが、例えば共通パーツ用パッケージとアプリ本体のパッケージそれぞれでslangを用いる場合に注意が必要です。
slangを用いる際にはTranslationProvider
でラップする必要があるので、別パッケージのslangを用いたい場合もそのクラスのTranslationProvider
でさらにラップする必要があります。
ただその場合クラスの衝突が発生します。
回避方法
以下のissueのようにasを用いるなどして表記を分けてあげれば良いだけです。
import 'package:package1/i18n/translations.g.dart' as package1;
import 'package:package2/i18n/translations.g.dart' as package2;
void main() {
runApp(
package1.TranslationProvider(
child: package2.TranslationProvider(
child: const MyApp(),
),
),
);
}
しかし、以下のような書き方もできます。importの際にはas
だけではなく個別クラスだけを取得するshow
、逆に個別クラスは排除するhide
があるのでそれを用います。
import 'package:themes/themes.dart' as theme show TranslationProvider;
import 'package:themes/themes.dart' hide TranslationProvider;
// ...
TranslationProvider(
child: theme.TranslationProvider(
child: const MyApp(),
),
),
結局やっていることはissueと大差ないのですが、せっかくならこう言う書き方もありますよと言う紹介でした。
ちなみにSlangはyaml形式でワードの定義ができる点もおすすめです。