😺

Flutter でアーキテクチャについて考えてみる

2023/07/27に公開

初めに

新しく参画したプロジェクトでネイティブアプリの新規開発をすることになりました。

今までの開発では MVVM を使う現場が多く、MVVM とその他のアーキテクチャとの比較については軽く調べる程度でした。

今回の開発では0からアプリを開発していくので、まずはソフトウェアアーキテクチャを選定し、ディレクトリ構成を練っていく必要があります。
いい機会なので改めて Flutter 開発においてソフトウェアアーキテクチャの種類や、それぞれの特徴を調べてみます。

今回は下記の順番でやっていきます。

  1. そもそもなぜアーキテクチャを選定する必要があるのか
  2. ネイティブアプリ開発においてよく使われるアーキテクチャを調査
  3. 各アーキテクチャの概要
  4. アプリの特徴
  5. 選定したアーキテクチャ

以後、説明を簡易的にするため、ソフトウェアアーキテクチャのことをアーキテクチャと書きます。

そもそもなぜアーキテクチャを選定する必要があるのか

こちらの記事がとても参考になりました。
https://zenn.dev/nameless_sn/articles/what_is_software_architechture

こちらの記事では「ソフトウェアアーキテクチャを学ぶべき理由」について言及されていますが、激しく同意します。
下記は記事の引用です。

なぜ私たちプログラマーは、ソフトウェアアーキテクチャを学ぶべきだろうか?

こちらに関する解答は個人差があるものの、私が考える最大の理由はプログラマー間で共通認識を作れることだと考える。アーキテクチャはコードを書く上での土台となる重要な概念である。土台が強力であれば、そこまで技術力の高くないプログラマーでもメンテナンス性の高いコードを書ける可能性が高まるのだ。

ネイティブアプリ開発においてよく使われるアーキテクチャを調査

アーキテクチャ 特徴
MVC Model(データ)、View(UI)、Controller(ロジック)を分けて考える方法。
MVVM データバインディングに重点を置いたアーキテクチャで、ViewModel が View と Model の間の接続を担当。
MVP ViewとModel の間に Presenter を配置。Presenter は View からのユーザーインタラクションを受け取り、Model を更新する。また、Model からデータを取得し、Viewに表示する。これにより、View と Model の間の依存関係がなくなります。
Clean Architecture アプリケーションを複数のレイヤーに分割する。各レイヤーは独立していて、内部レイヤーから外部レイヤーへの依存関係のみが存在する。これにより、各レイヤーのテストとメンテナンスが容易になる。

この時点で、MVC、MVVM、MVP が似ているので少しわかりにくいです。
それぞれの特徴をふかぼっていきます。

MVC、MVVM、MVP を調べる前に

MVC や MVVM、MVP について調べる前に、これらの共通部分である「M」と「V」について役割を調べました。

Model とは
アプリの「情報」を管理します。例えばアプリ内で扱うデータや、そのデータをどう操作するか(加算、減算といったビジネスロジック)のルールを担当します。
また、DB とデータのやり取りをしたり、DB から取得したデータをアプリ内で扱いやすい形式に変換したりする部分も担当します。

View とは
ユーザーが実際に見る UI の部分を担当します。
表示以外にも各アーキテクチャによって、どこまでのロジックを担当するのかが若干変わります。

上記の役割を前提に各アーキテクチャを調べていきます。

MVC

MVC は最も基本的なアーキテクチャで、Model(データ部分)、View(見た目部分)、Controller(情報のやり取り部分)の3つの要素に分割します。

Controller はあくまで View と Model の橋渡し役です。
なので基本的にはデータの保持はせず、ビジネスロジックも持ちません。
ユーザーの入力に応じて Model と View を制御する役割を担います。

また、Model が View や Controller から分離されているという特性があり、以下のメリットがあります。

  1. 再利用性
    同じ Model を異なる View や Controller で再利用できるため、新しい画面を作成する時は View と Controller を作成するだけで、Model は再利用することができます。
  2. 単体テストの容易さ
    Model が独立しているため、Model の単体テストが行いやすくなります。
  3. メンテナンスの容易さ
    Model の変更が View や Controller に影響を与えにくくなるため、メンテナンスが容易になります。

しかしこれらのメリットは、MVVM や MVP といった他のアーキテクチャでも同様にあります。
それでも MVC が選択される理由はいくつかあります。

  1. シンプルさ
    MVC は非常にシンプルで直感的なアーキテクチャです。Model、View、Controller の3つのコンポーネントに分けるだけで、それぞれの役割が明確です。このシンプルさは、小規模なアプリケーションやプロトタイプの開発においては、迅速な開発と簡単なメンテナンスを可能にします。
  2. 学習コストの低さ
    MVCはその概念がシンプルであるため、新たに学習するコストが比較的低いです。これは、新たにチームに参加したメンバーや初めてアプリケーション開発を行う開発者にとっては大きなメリットとなります。

デメリットは以下になります。

  1. View の変更が Controller に影響を与えやすい
    View と Controller は密接に関連しているため、View の変更が Controller に影響を与えやすいという問題があります。
    これは、シンプルなアプリケーションでは問題になりにくいですが、複雑なアプリケーションではテストやメンテナンスが難しくなる可能性があります。
  2. Model が膨大になる
    Model がビジネスロジックを担当するためコード量が多くなり、可読性が低下する可能性があります。

まとめると以下になります。

  1. 学習コストが低い
  2. シンプルで可読性が良い

MVVM

MVVM についてこちらの記事の解説が個人的にしっくりきました。
https://wasabeef.medium.com/flutter-を-mvvm-で実装する-861c5dbcc565

MVVM では、MVC の Controller の代わりに ViewModel が導入されます。
ViewModel は View に表示するデータを保持し、Model と View の間で情報をやり取りします。
ViewModel が MVC の Controller と違うところは以下です。

  1. データの保持をする
  2. ビジネスロジックを担当する(例外あり[1]

MVVM のメリットは以下になります。

  1. データバインディング
    MVVM の最大の特徴はデータバインディングです。
    データバインディングとは ViewModel のデータが変更されると、自動的に変更されたデータが View に反映される(またはそれを実現する仕組みの)ことです。
    これにより、View と ViewModel の間の同期を手動で行う必要がなくなり、コードの量が減ります。
  2. View の状態管理
    MVC では View 自身が View の状態を管理するのに対して、MVVM では ViewModel が View の状態を管理します。これにより、アプリケーションの異なる部分で同じデータを使用する場合でも、そのデータが一貫性を保つことができます。
  3. テストの容易さ
    ViewModel は View から独立しているため、UIに依存しない形で ViewModel のテストを行うことができます。これにより、ユニットテストが容易になります。
  4. View の複雑さの軽減
    ViewModel がビジネスロジックと状態管理を担当するため、View は表示のためのロジック(UI ロジック)だけを持つことになります。これにより、View の複雑さが軽減されます。

まとめると以下になります。

  1. ViewModel はデータの保持とビジネスロジックの処理を担当する
  2. ViewModel はデータバインディングと View の状態管理をサポートすることで、View の複雑さを軽減
  3. MVC の Controller はあくまで View と Model の「橋渡し役」なので ViewModel とは役割が明確に違う
  4. MVC では View 自身が View の状態を管理するのに対して、MVVM の場合は ViewModel が View の状態を管理する

MVP

MVP (Model-View-Presenter) は MVC の改良版とも言えます。
Model(データ部分)、View(見た目部分)、Presenter(情報のやり取り部分)の3つの要素に分割します。

ここまで説明してきた MVVM、MVC とは違い Presenter は View の UI ロジックも担当します。
Flutter で説明すると、ダイアログやスナックバーの表示などの UI ロジックをどこが担当するか、それぞれのアーキテクチャで比較すると分かりやすいです。

  • MVC: View が担当
  • MVVM: View が担当
  • MVP: Presenter が担当

MVP のメリットは以下になります。

  1. テストの容易さ
    MVC と違い MVP では、View と presenter がしっかりと分離されています。
    これにより、それぞれの部分を独立してテストしたり、再利用したりすることが容易になります。
  2. View の再利用性
    MVP では、View は一般的にインターフェースとして定義され、Presenter はこのインターフェースを介して View と通信します。
    これにより、異なる View で同じ Presenter を再利用することが可能になります。
    先ほどのダイアログの例の通り、同じロジックを持つが、異なる UI を必要とする View が複数ある場合、それぞれの View に対して同じ Presenter を再利用することができます。
    これは、MVC や MVVM では実現しづらいメリットです。

しかし、MVP には以下のデメリットもあります。

  1. コードの複雑さ
    Presenter がすべての通信を仲介するため、コードが複雑になることがあります。特に、大規模なアプリケーションでの使用には注意が必要です。

まとめると以下になります。

  1. Presenter が UI ロジックも担当するため、View はレイアウトだけを担当するシンプルな形にできる
  2. Presenter の負担が大きくなり、コードが複雑になる可能性がある

Clean Architecture

Clean Architectureは、アプリケーションを Entities、Use Cases、Interface Adapters、Frameworks and Drivers の4つのレイヤーに分けます。
Clean Architecture といえばこの図が有名ですね。

各レイヤーは独立しており、特定の役割を持っています。

  • Entities(黄色部分)
    アプリの中心にあるもので、それが何をすべきか(アプリの本質的なルールや原則)を定義します。例えば、ユーザーやアイテムなど、アプリケーションが取り扱う重要な情報やその情報の振る舞いを表すことがあります。

  • Use Cases(赤色部分)
    アプリが何をするか(具体的な機能)を定義します。ユーザーが何を行うことができるかを示し、ビジネスルールを実行します。例えば、「商品をカートに追加する」や「新しいメッセージを送信する」などの操作がこれに該当します。

  • Interface Adapters(緑色部分)
    アプリと外の世界(データベースや UI など)との間の翻訳役を果たします。この部分で、アプリ内部のデータ形式を外部が理解できる形式に変換したり、その逆を行ったりします。具体的には、APIから取得したデータをアプリ内で扱える形式に変換したり、ユーザーの操作をアプリが理解できる指令に変換したりします。

  • Frameworks and Drivers(青色部分)
    具体的な技術(データベースやフレームワーク)を扱う部分です。この層が外部の世界と直接対話を行います。例えば、データベースからデータを取得したり、APIにリクエストを送信したり、ユーザーからの入力を受け取ったりします。

Clean Architecture には以下の注意点があります。

  1. 画像はあくまで例えであり、必ずレイヤーを4層に分割しろというわけではない
  2. 「依存性のルール」、「関心の分離」、「依存関係逆転の法則」などを重要視

徹底的に Clean Architecture について考え抜かれている記事がありましたので紹介します。(長めですがとても参考になります)
https://www.nuits.jp/entry/easiest-clean-architecture-2019-09

Clean Architectureのメリットは以下になります。

  1. テストの容易さ
    各レイヤーが独立しているため、それぞれの部分を個別にテストすることが容易になります。
  2. 変更に強い
    データベースの変更やUIの変更がビジネスロジックに影響を与えることが少ないため、変更に強いコードを書くことができます。
    よって保守性とスケーラビリティを向上させ、長期的な費用対効果に寄与します。

しかし、Clean Architectureには以下のデメリットもあります。

  1. 学習コスト
    Clean Architectureは他のアーキテクチャに比べて複雑であるため、学習コストが高いです。
  2. コードが冗長
    Clean Architecture はレイヤーの分離と依存性の逆転を重視するため、実装が複雑になる可能性があります。
    具体的な例として、新しい機能を追加する際に、どのレイヤーに配置すべきか、どのように依存性を管理すべきかを考える必要があります。
  3. 初期開発コスト
    各レイヤーを明確に分離するための初期設定が必要であり、これにより初期開発コストが高くなる可能性があります。

まとめると以下になります。

  • 大規模な開発に向いている
  • 各レイヤーが独立しているためテストがしやい、変更に強い、チームメンバーはそれぞれが関連するレイヤーの作業に集中できるなど多くのメリットがある
  • 実装が複雑で初期開発コストや学習コストが高い

アプリの特徴

ここまで各アーキテクチャを簡単にまとめてきました。
この章では実際にアーキテクチャを選定するための判断材料として、アプリの特徴を洗い出します。
主な特徴は以下になります。

  • アプリの規模は小規模(今後の拡張で中規模)である
  • Flutter エンジニア以外もコードを読んだり、変更することがある
  • チャット機能があり、データが頻繁に変更される

これらの特徴からアーキテクチャを選定します。

選定したアーキテクチャ

選ばれたのは MVVM でした。
理由としては以下になります。

  • アプリの特徴
    今回のアプリは小規模 ~ 中規模のアプリであり Clean Architecture は学習コストの観点から排除しました。
    また、チャット機能があるので MVVM の特徴であるデータバインディングと相性が良さそうでした。

  • プロジェクトメンバー
    プロジェクトメンバーの Flutter 開発者が MVVM の経験があるためスムーズに開発が行えるというメリットもあります。
    また、Flutter エンジニア以外もコードの確認や簡単な修正をする可能性があるのでわかりやすく情報が多いアーキテクチャを採用したかったです。
    MVVM はそれぞれのレイヤーの負担割合がバランスよく、可読性も良いと判断しました。[2]

今回は特殊な要件[3]がないことやプロジェクトの規模感から Clean Architecture は不採用になりました。
ただ、人気のあるアーキテクチャであることは確かなので、今後のためにも学習しておいた方が良さそうだと感じました。

まとめ

そもそもなぜアーキテクチャを選定する必要があるのかの章でも解説しましたが、アーキテクチャを採用する大きな理由はプログラマー間で共通認識を作れることだと思います。

今後の拡張性がないのに複雑な構造を採用してはいけませんし、逆も然りです。
各アーキテクチャのメリット、デメリットで度々出てきた学習・開発コストや拡張性はトレードオフの関係にあります。
プロジェクトの要件に応じて適切に選定するのはアーキテクチャの知識や実際に使用した経験が大事だと痛感しました。

と偉そうにいっても筆者はまだまだ知識不足です。
この記事がアーキテクチャの選定に役立つものになれば・・・という気持ちも込めて書きましたがそうはなりませんでした。
それぞれのアーキテクチャはあくまでも概念で、とてもふわっとしており、調べても個人個人の解釈や使い方が違うので頭を悩ませます。
今後の開発経験や自己学習で、より精進していきます。

ここまで読んでいただき、ありがとうございました🙇🏻‍♂️

脚注
  1. 「ビジネスロジックの処理を担当する(例外あり)」と書いた理由は、MVC の Controller がビジネスロジックを含むことがあるからです。
    しかし、Controller がビジネスロジックを含むことはあまり推奨されていないみたいです。
    これは、ビジネスロジックが Controller に含まれると、そのロジックが特定の View に依存する可能性があり、再利用性とメンテナンス性が低下するからだそうです。 ↩︎

  2. 完全に筆者の経験不足 + MVVM 贔屓が入っています。 ↩︎

  3. UI もしくは DB を変更しただけの別アプリを作成するなど ↩︎

GitHubで編集を提案

Discussion