FlutterでMicro-Frontends:Full-Cycle Developer
マイクロフロントエンド(Micro-Frontends)とは
バックエンド開発でマイクロサービスという考え方が普及してきた。バックエンドの各種サービスを分割して開発する手法である。一方、まだ一般的ではないが、マイクロフロントエンドという考え方がある。
基本的な考え方はバックエンドにおけるマイクロサービスと同じである。フロントエンドの場合、各パート(サブチーム)が独立してネットワーク接続から、DBの操作、 UIの実装、さらにはE2Eテストまで実施する。タスクがサブチーム内で完結しており、他のサブチームの影響を受けないという特徴がある。
フルサイクルデベロッパー(Full-Cycle Developer)
バックエンドからフロントエンドまでこなす開発者のことをしばしばフルスタックデベロッパーと呼ぶ。程度の差こそあれ、長く開発業界にいれば、両方の経験を積む機会があるだろう。なので自分のことをフルスタックデベロッパーだと思う開発者は多いと思う。
ここでいうフルサイクルデベロッパーとはネットワーク接続からローカルDB操作、ビジネスロジック記述に、UI調整、各機能に対するUTに加え、E2Eテストまでこなせる開発者のことである。
先ほど書いたようにマイクロフロントエンドの枠組みでは各パートが独立して(相互に依存しないで)作業を進めるため、このフルサイクルデベロッパーが必要なのである。
1人ではできないにしても、少なくとも各チームはフルサイクルが実施できるように構成されなければいけない。
Flutterでフルサイクルデベロッパーになるには
Flutterで大規模なプロジェクトを作成する機会は、ネイティブより少ないかもしれない。だがスタートアップ企業が自社サービスをFlutterで作成、営業規模が大きくなるにつれて自然と規模が膨らむケースはあるだろう。また特定の機能を利用するためにはネイティブの機能を利用しなくてはいけないケースが出てくる。
Flutterにおけるマイクロフロントエンドの実現
マイクロフロントエンドの思想をFlutterプロジェクトに適用する際のポイントは以下の通り:
1. モジュール化
Flutterアプリを機能ごとに独立したモジュールに分割することで、ネイティブコードの統合やメンテナンスを容易にする。
- 各モジュールを独立して開発、テスト可能にする。
- 必要に応じて、モジュールごとにネイティブコードを含める。
2. プラグイン(Platform Channel)を活用
Flutterはプラットフォーム固有のコードを呼び出すために、Platform Channelを提供している。
- 必要なネイティブ機能をDartコードでラップし、Flutterから簡単に呼び出せるようにする。
- プラグインとして切り出し、再利用可能にする。
3. 疎結合なアーキテクチャ
- 各モジュール間の依存を最小限にし、モジュールごとの開発やデプロイを独立して行えるようにする。
- メッセージング(例えば、イベント駆動アーキテクチャ)を活用して、モジュール間のデータ交換を行う。
4. 統合戦略
- ネイティブコードとFlutterコードの混在を適切に管理する。
- Flutter Module Integration: 既存のネイティブアプリにFlutterモジュールを追加する。
- Flutterからネイティブを呼び出す: カスタムプラグインを使って、必要なネイティブAPIを呼び出す。
- Platform View: ネイティブUIコンポーネントをFlutter内で表示する(例:Google Mapsなど)。
5. チーム間の役割分担
- Flutterチームとネイティブチームがそれぞれ独立して作業できる環境を整備。
- 共通インターフェースを設計し、モジュール間の連携を効率化する。
マイクロフロントエンドを採用する利点
- 柔軟性の向上: 特定の機能のみネイティブで実装できるため、必要な部分だけを最適化できる。
- スケーラビリティ: 新しいモジュールを容易に追加可能。
- 再利用性: 共通機能をプラグインとして再利用可能。
- ネイティブ依存の管理: プラットフォーム固有のコードの影響範囲を最小化。
ネイティブ統合をマイクロフロントエンドで
以下に、Flutterから直接利用できない、または公式プラグインがまだ十分ではないためにネイティブ統合が必要となる主な機能を挙げる。
1. デバイス固有のハードウェア機能
- Bluetooth LE:
- Flutterのプラグイン(例:
flutter_blue
)は存在するが、すべての機能をサポートしているわけではない。 - 特に、高度なBluetoothプロファイル(Audio, BLE Meshなど)を利用する場合、ネイティブコードが必要になる場合がある。
- Flutterのプラグイン(例:
- NFC (Near Field Communication):
- FlutterにはNFCを扱ういくつかのライブラリはあるが、特定のカードリーダーやカスタムプロトコルを使用する場合、ネイティブコードでの対応が必要。
- センサーの高度な利用:
- 加速度センサー、ジャイロスコープ、マグネトメーターなどの標準的なデータ取得は可能だが、低レベルなセンサーAPI(センサーのキャリブレーションなど)を使用する場合、ネイティブ統合が必要になる。
2. プラットフォーム固有のUIコンポーネント
- WebViewの高度なカスタマイズ:
- Flutterには公式プラグイン
webview_flutter
があるが、高度なカスタマイズや特定のブラウザエンジンを必要とする場合、ネイティブ統合が必要になることがある。
- Flutterには公式プラグイン
- ネイティブのダイアログや通知:
- Flutterでも基本的なダイアログや通知は可能だが、ネイティブの通知API(例: Androidの通知チャンネルやiOSのカスタム通知UI)を使用する場合、ネイティブコードが必要。
- マップアプリの高度な統合:
- Google MapsやApple MapsはFlutterでもプラグインが提供されているが、カスタムのマップスタイルや高度なイベント処理(例: 3D地図の統合など)では、ネイティブの統合が必要。
3. バックグラウンド処理とサービス
- バックグラウンドタスク:
- Androidのサービス(Foreground Service)やiOSのバックグラウンドフェッチなどの実装は、Flutterから直接利用するのが難しい場合がある。
- 例えば、バックグラウンドでの長時間実行タスク(位置情報の継続取得やデータ同期)にはネイティブコードが必要。
- Push通知の高度な設定:
firebase_messaging
は基本的なPush通知をサポートするが、カスタムアクションや通知スケジューリング、プラットフォームごとの詳細な設定にはネイティブコードが必要。
4. マルチメディア機能
- 高度なオーディオ処理:
- 音声エフェクト、リアルタイムのオーディオストリーミング、音声認識API(例: Google Speech-to-TextやSiriの統合)はネイティブでの実装が必要。
- 動画エンコーディング/デコーディング:
- Flutterでは動画再生は可能だが、高度なエンコーディング(例: H.265コーデックの対応)や、特定のデバイスでの最適化にはネイティブコードが必要。
- カメラの高度な制御:
camera
プラグインは標準的なカメラ操作をサポートするが、手動フォーカス、特殊な解像度設定、またはマルチカメラの使用ではネイティブコードが必要。
5. デバイス管理とセキュリティ
- 生体認証の高度な統合:
- 指紋認証や顔認証(Face ID/Touch ID)についてはFlutterでもサポートされているが、セキュリティが高度に求められるシナリオ(例: ハードウェアセキュリティモジュールとの連携)ではネイティブコードが必要。
- デバイス管理機能(MDM):
- 企業向けのモバイルデバイス管理(MDM)をFlutterで直接サポートするのは難しく、ネイティブの管理APIを使用する必要がある。
6. プラットフォーム特有のAPIやライブラリ
- AR/VRの高度な統合:
- Google ARCoreやApple ARKitのフル機能を利用するには、ネイティブコードでの対応が必要。
- プロプライエタリなSDKの統合:
- 一部のサードパーティ製SDK(特に広告、アナリティクス、特定デバイス用SDK)は、Flutterのプラグインが提供されていない場合が多く、ネイティブで直接組み込む必要がある。
7. その他
- カスタムファイルシステムの利用:
- 特殊なファイルシステムやネットワークドライブの統合(例: SMBやNFS)にはネイティブ統合が必要。
- プラットフォームの将来の変更:
- 新しいOS機能やAPIが追加された場合、Flutterプラグインが追いつく前にネイティブ統合が求められることがある。
結論
Flutterは多くのシナリオで強力なクロスプラットフォーム開発を可能にするが、特定のプラットフォーム固有の機能や高度なカスタマイズには、ネイティブコードの統合が必要である。このため、Flutterでの開発においては、ネイティブの知識を持つことが重要である。これが、Flutter開発におけるフルサイクルデベロッパーの大切な要素となる。
マイクロフロントエンドの考え方でプロジェクトを構成しておけば、こういったネイティブの機能を取り込む事が容易になる。そして新たな機能追加が容易にできるようになるのだ。