Ⓜ️

【Flutter】Melosで機能毎にパッケージを分割する

2024/01/26に公開

サンプル

今回作成したサンプルアプリはこちらになります。
https://github.com/kktaro/melos_sample

はじめに

Flutter、もといDartにはMelosという、複数パッケージを管理するためのツールが存在しています。これを使用してFlutterプロジェクトのアーキテクチャをいい感じに分割できそうでは?と思ったので色々調べて試してみました。

やりたいこと

私はFlutterの開発でNow in AndroidDroidKaigi2023 Appのように機能やレイヤ毎にディレクトリを分割していました。
しかし、DartにはKotlinでいうinternal修飾子のようなパッケージプライベートを提供する機能は存在しないため、常にどこに依存しているか、きちんと機能単位で独立しているかを意識しながらコーディングする必要がありました。
そこで、機能毎にパッケージとして分割することでその煩わしさから抜け出してみようと思います。

Melosとは

公式サイトによると、

Melos is a CLI tool used to help manage Dart projects with multiple packages (also known as mono-repos).
Splitting up large code bases into separate independently versioned packages is extremely useful for code sharing. However, making changes across many repositories is messy and difficult to track, and testing across repositories gets complicated. Melos helps solve these issues by allowing multiple packages to work together within one repository, whilst being totally independent of each other. Features include:

  • Automatic versioning & changelog generation.
  • Automated publishing of packages to pub.dev.
  • Local package linking and installation.
  • Executing simultaneous commands across packages.
  • Listing of local packages & their dependencies.

ということで、モノレポ構成のプロジェクト管理を補助してくれるツールになります。

Melosの機能の内、

  • ローカルパッケージのリンクとインストール
  • パッケージ間での同時コマンド実行
  • ローカルパッケージとその依存関係の一覧表示

を活用することで、Flutterプロジェクトのパッケージ分割がうまくできそうです。

Melosを使ってアプリを作ってみる

サンプルとしてカウントアップ/ダウンするだけのアプリを作成しました。
Melosを使う構成の参考にしてもらえればと思います。
https://github.com/kktaro/melos_sample

使用環境は以下になります。

fvm: 2.4.1
melos: 4.0.0

プロジェクト作成

まずは以下のコマンドでMelosを有効化します。

dart pub global activate melos

続いてプロジェクトを作成します。
Melosを使用する場合はプロジェクトルートになるので、ルートにmelos.yamlを追加しつつ、必要な設定を公式ドキュメントを参考に記述します。
今回はfvmを使用しますので、sdkPathの記述を追加しています。
https://github.com/kktaro/melos_sample/blob/main/melos.yaml#L1-L5

パッケージ作成

続いて必要なパッケージを作成します。
今回は以下の単位でパッケージを分割し、ルールに沿って保存先を決定します。

  • アプリ
    アプリに関する機能(ルーティング等)
  • UI
    UIを構成するWidgetやテーマ
  • モデル
    プロジェクトで使用するデータクラス
  • カウント
    カウントアップを実現するためのロジック
  • メインページ
    アプリのメインとなるページ
  • テスト
    テスト時に使用する便利処理

melos list --graphでJSON形式でパッケージ間の依存関係を出力することができます。

{
  "count": [
    "model",
    "testing"
  ],
  "main": [
    "count",
    "model",
    "ui",
    "testing"
  ],
  "melos_sample_app": [
    "main",
    "model",
    "ui",
    "count"
  ],
  "model": [],
  "testing": [],
  "ui": [
    "model"
  ]
}

画像で欲しい場合はmelos list --gvizとすることでDOT言語で出力することができるので変換することで表示できます。こちらの方がぱっと見で依存関係が把握できてよさそうです。(DOT言語初めて知った…🤔)
graph

スクリプトの作成

続いてmelosのスクリプトを作成します。
複数のパッケージが存在する場合、通常はパッケージごとにflutter pub getflutter build runner build run -d等のコマンドを実行する必要があり、非常に面倒です。これを解決してくれるのがスクリプトで、melos.yamlにコマンドを定義して、melos run ~~とすることで全パッケージに向けてコマンドを実行することができるようになります。(公式ドキュメント)
https://github.com/kktaro/melos_sample/blob/main/melos.yaml#L6-L41

ついでにGutHub Actionsでリントとテストのワークフローを設定しておくと良いです。
ワークフロー内でもmelosに定義したスクリプトを使用することができます。今回はlinttestを実行することでコードの品質を担保するワークフローを作成しています。
https://github.com/kktaro/melos_sample/blob/main/.github/workflows/code_quality.yaml

実装する

ここまででプロジェクトの環境整備はできましたので、あとはひたすら実装するのみです。新しくファイルを作成する際にはどのパッケージにいるべきかを考えて、適切な場所に置くように心がけましょう。

まとめ

Melosを使うことで、比較的容易にマルチパッケージ構成のアプリを作ることができました。
小規模なアプリではそこまで恩恵はなさそうですが、規模が大きくなってもコードを綺麗に保ちやすくなるかと思います。

今回の構成だと、外部のライブラリへの依存が各パッケージに分散してしまっており、バージョン管理が面倒になりそうです。この辺を解決できる方法があればまた記事にできればと思います。

株式会社ガラパゴス(有志)

Discussion