[Flutter/Ios/Android]モダン設計比較 (DI依存性/非同期処理/データストリーム/通信/ハードウェアフレームワーク)
はじめに
Flutter/Ios/Androidにおいてモダンな構成と相違点を簡潔にまとめていきたいと思います。
設計 アーキテクト
どの言語を使用し、プロジェクトを始めるかに関わらず、アーキテクトのベースになるのは、クリーンアーキテクチャとドメイン駆動設計に基づいた設計になると思われます。
クリーンアーキテクチャ
- エンティティ(Entities)
- ユースケース(Use Cases)
- インターフェースアダプター(Interface Adapters)
- フレームワークとドライバー
DDD - エンティティ(Entities)
- 値オブジェクト(Value Objects)
- アグリゲート(Aggregates)
- リポジトリ(Repositories)
- サービス(Services)
クリーンアーキテクチャは責務の分離、DDDは責務のカプセル化と捉えていただければ、理解しやすいかと思います。
ネイティブアプリ開発においては、これらを用いたレイヤードアーキテクチャ、単方向データフロー、State Hoistingがモダンな開発において基本になってきます。
責務のカプセル化を行いプロバイダツリーを構成する。
昨今では技術の進化が早く、外部SDKや、生成AIの導入など、拡張性に優れた設計をネイティブアプリ開発でも推奨されているように、個人的には感じており、責務のカプセル化を行いプロバイダツリーを構成、DIにより依存性を注入するというのが今、最もモダンな設計であると言えると思う。
裏付けるように、IosのMV、AndroidのNowInAndrod、FlutterのRiverpodなどが推進されてきている。
※Storeをプロバイダツリーのどこに持たせるかの違いはある。
中規模設計OS毎のアーキテクチャ/依存性グラフ
Ios | Android | Flutter |
---|---|---|
MV/Flux | MVVMマルチモジュール +Flux | Riverpod |
大規模プロジェクトOS毎のアーキテクチャ
Ios | Android | Flutter |
---|---|---|
VIPPER | MVVMマルチモジュール+クリーンアーキテクチャ | 向いていない |
大規模プロジェクトになると、APPのシステムマネージャのContextをサブクラスへデリゲートして、サブクラス内で、責務のカプセル化を行いプロバイダツリーを構成するのがモダンと言えそうである。
DI依存性
基本的にプロバイダツリーの最上位に位置し、どのようにBind/Inject/Providesするか設定することにより、シームレスな機能提供ができる。
Ios | Android | Flutter |
---|---|---|
プロトコル | Dagger Hilt | Riverpodの依存関係注入機能 |
Ios プロトコル
パッケージ管理においても、一括でライブラリなど入れ、どこからでもなんでも参照でき利便性が高いが、プロジェクトが荒れやすい傾向にあると思う。
しっかり切り分けて、依存性を入れたいところではあるが、目立ったライブラリ等もなく、プロトコルで、カスタムしていくのが現実的と言えると思う。自由度が高いというのがIosネイティブのメリット、面白さであると思うが、ある意味で一番難易度が高いように思う。
Android Dagger Hilt
Dagger Hiltによってインターフェースが提供されているので、依存関係の注入タイミングを理解していれば、かなりシームレスな実装が可能となっている。
Flutter Riverpod
Riverpodで便利すぎる。しかし、なんでもRiverpodでできてしまうので、AndroidのDagger Hiltを理解した上で、使用するとさらに精密性が上がる気がしています。
非同期通信
Ios | Android | Flutter |
---|---|---|
IOS17以降 Observation/Combine | StateFlow/FlagmentパターンだとLiveData | RiverpodのStateProvider |
Ios Observation/Combine
Combine @Publishedで発行 @ObservedObject を使ってUI側で感知
ただオブジェクト単位での感知になるので、プロパティ単位でUIの変更が多いと再描画が過多になる傾向
IOS17以降 Observationdでプロパティ単位の感知
Android StateFlow
StateFlow を用いUI側でCollectで感知
FlagmentパターンだとLiveDataのライフサイクルオブザーバ
Flutter Riverpod StateProvider
Riverpod StateProviderにて発行
UI
宣言的UI
Ios | Android | Flutter |
---|---|---|
SwiftUI | JetpackCompose | Widget |
Ios SwiftUI
Human Interface Guidelines準拠
Android JetpackCompose
Material3準拠
Flutter Widget
Material component widgets
通信
networkモジュールでサービス、dataSource,
DataモジュールでInterface,Implにてデータ取得
networkモジュールにてカプセル化
非同期データフローでデータストリーム
Ios | Android | Flutter |
---|---|---|
Alamofire | Retrofit2(okfttp3のラッパー) | Retrofit(dioのラッパー) |
ハードウェアの固有のフレームワークへのアクセス
メデイア/BLE/カメラ/ストレージなどハードウェアの固有のフレームワークへのアクセスは少し概念が異なってくる
IOS 拡張
Android 依存
Flutter メソッドチャンネルにて呼び出し
まとめ
ネイティブアプリという領域においては、すべて同じだと思うので、差分を理解しながら、すべての言語において、最適解を見つけていきたいです!
よかったらこちらも見てください!
Discussion
これって昔からあるアプリが例にだされてるんですかね?
YOUTRUSTさんは使っていたそうですが、FutureProviderやAsyncNotifierをもし最近ので作ろうなら、使いませんか?