Slide 1

Slide 1 text

Flutter アプリの将来のインフラ移行に備える 疎結合なソフトウェア・アーキテクチャ 株式会社Hakali 取締役CTO Takahiro Ikeuchi - 池内 孝啓 FlutterKaigi 2022

Slide 2

Slide 2 text

Agenda 自己紹介 - 2分 インフラ移行にどう備えるか - 5分 疎結合なソフトウェア・アーキテクチャを目指して - 20分 アンチパターン2選 - 5分 まとめ - 3分 1. 2. 3. 4. 5. 💡 資料およびサンプルコードは公開いたします 2

Slide 3

Slide 3 text

池内 孝啓(いけうち たかひろ) デジタル認知行動療法アプリ Awarefy CTO。 https://github.com/iktakahiro https://iktakahiro.dev/ 自己紹介 @iktakahiro https://www.awarefy.app 3

Slide 4

Slide 4 text

Android/iOS 両対応のプロダクト開発のため、2019年9月に Flutter を採用、以来となるFlutter歴3年。 Flutterと私 https://pub.dev/packages/iso_calendar https://pub.dev/packages/fargon2 OSS (pub.dev) 4

Slide 5

Slide 5 text

本日のテーマ インフラ移行にどう備えるか 5

Slide 6

Slide 6 text

少人数でプロダクトを開発してリリースまで もっていかなくちゃ... Firebase や Supabase がいいかも.... ベンダーロックインが心配だな.... 6

Slide 7

Slide 7 text

実録!Firebase to AWS 2020年5月にリリース時点でバックエンドは Firebase の 完全サーバーレス 2022年7月にAWSに移行 Amazon EKS x Amazon Aurora Serverless v2 サーバーサイドアプリを運用開始 7

Slide 8

Slide 8 text

Firebase 選定の理由 - 2019年 関心事を Flutter に限定したかった リリースまでのスピード! SDK もあるしよさそう 8

Slide 9

Slide 9 text

Firebase 移行の理由 - 2021年 ビジネスロジックの複雑化への対応 データマイグレーション時の負担の解消 運用面の諸々の強化 (バックアップ、リストア、セキュリティ etc) RDB 化を決意。 ※ ニーズとの相性の問題。Firestore はいいサービスです! 9

Slide 10

Slide 10 text

新インフラ - 2022年 クラウドインフラ選定に関する 詳細は割愛 ※

Slide 11

Slide 11 text

おもな変更点 バックエンドアプリのスクラッチ実装(Go + gRPC) インフラ基板の刷新(Amazon EKS) データストアの刷新(Amazon Aurora Serverless v2) ※ データマイグレーションの話は今回取り扱いません 11

Slide 12

Slide 12 text

Before モバイルアプリ Firestore 12

Slide 13

Slide 13 text

After モバイルアプリ Web App PostgreSQL 13

Slide 14

Slide 14 text

インフラ移行にどう備える(た)か? 14

Slide 15

Slide 15 text

結論 15

Slide 16

Slide 16 text

結論 インフラレイヤーの抽象化をしておく 16

Slide 17

Slide 17 text

結論 17 なるべく早期(できれば最初)から!

Slide 18

Slide 18 text

疎結合な ソフトウェア・アーキテクチャを目指して 18

Slide 19

Slide 19 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/ 依存性逆転の原則を用いてインフラを 最も外側に置く オニオン・アーキテクチャ テスト

Slide 20

Slide 20 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ テスト Flutter (Widget) = プレゼンテーション層 Firebase などの mBaaS = インフラ層 20

Slide 21

Slide 21 text

Flutter Widget ユースケース リポジトリ(インターフェース) コードの依存関係 実際のデータの流れ Flutter Widget ユースケース リポジトリ(実装) 21

Slide 22

Slide 22 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ テスト Entity Value Object Repository Service ドメイン層 ドメインモデルと リポジトリインターフェースの定義

Slide 23

Slide 23 text

ドメインモデル(Entity) ※ モデルやコードはすべて簡略化しています

Slide 24

Slide 24 text

リポジトリ(永続化処理)の抽象化 Dartとインターフェース : https://dart.dev/samples#interfaces-and-abstract-classes https://dart.dev/guides/language/language-tour#implicit-interfaces

Slide 25

Slide 25 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ テスト DB HTTPS RESTFul gRPC インフラ層 リポジトリの実装 25

Slide 26

Slide 26 text

特定のシステムに紐付くものはすべてインフラ RDB(MySQL, PostgreSQL) NoSQL(Firestore, MongoDB) HTTPS / RESTFul API gRPC / Protocol Buffers SMTP SDK インフラ ≒ 具体

Slide 27

Slide 27 text

Firestore 向けリポジトリ(永続化処理)の実装 クラス名なども具体的なインフラの命名をつけておくと後々わかりやすい

Slide 28

Slide 28 text

Firestore 向け DTO Firestore のデータ(Map)と ドメインモデルを相互変換する

Slide 29

Slide 29 text

Dart と DTO (Data Transfer Object) インフラ層のデータ構造とドメインオブジェクトをマッピング JSON とオブジェクトの相互変換が必要な場合 freezed パッケ ージ周辺のツールが便利 toJson, fromJson が生える immutable なデータクラスとしての振る舞い https://pub.dev/packages/freezed

Slide 30

Slide 30 text

リポジトリ(インフラ層)抽象化の意義 ドメインオブジェクトの隔離とテスタビリティの確保が主目的 副次的にインフラ変更に強くなる SOLID 原則 : 抽象に依存せよ 30

Slide 31

Slide 31 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ テスト ユースケース CQRS ユースケース層 ユースケースのインターフェースの 定義およびその実装

Slide 32

Slide 32 text

ユースケース(インターフェース) ここでも欠かさず抽象化。プレゼンテーション層のテストがやりやすく なります。 ※

Slide 33

Slide 33 text

ユースケース(実装) リポジトリのインターフェースに依存(実装には依存しない)

Slide 34

Slide 34 text

ユースケースの初期化と登録 のちほど取り出して使います。

Slide 35

Slide 35 text

ドメインオブジェクトからのインフラ層の隔離はOK NEXT : プレゼンテーション層 35

Slide 36

Slide 36 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ テスト UI(View) View Model Controller Routing プレゼンテーション層

Slide 37

Slide 37 text

View View Model Flutter Widget Riverpod プレゼンテーション層の View と View Model ※ 状態管理は Riverpod である必要はないです。他の選択肢でもOK

Slide 38

Slide 38 text

Flutter Widget ユースケース リポジトリ(インターフェース) コードの依存関係 実際のデータの流れ Flutter Widget ユースケース リポジトリ(実装) View Model (Riverpod) View Model (Riverpod)

Slide 39

Slide 39 text

Riverpod + StateNotifier Riverpod StateNotifier freezed の3点セット

Slide 40

Slide 40 text

View Model からユースケースを利用 ユースケースのインターフェースに依存(実装には依存しない) 40

Slide 41

Slide 41 text

Riverpod + Hooks View Model の メソッドと状態にアクセス

Slide 42

Slide 42 text

ドメイン ユースケース プレゼンテーション(UI ) インフラ インフラ層をふくめ 各レイヤーが疎結合な状態を達成 テスト バックエンドに関する変更の場合、 インフラ層のみの変更で対応可能 ※ もちろん理想的にいけば、の場合

Slide 43

Slide 43 text

インフラを差し替えてみよう

Slide 44

Slide 44 text

SQLite 向けリポジトリ(永続化処理)の実装 リポジトリを二重に実装する 意味はありませんが、例です SQLite のデータ(Map)と ドメインモデルを相互変換する

Slide 45

Slide 45 text

SQLite 向け DTO 45

Slide 46

Slide 46 text

これを...

Slide 47

Slide 47 text

インフラの差し替え!! こうする!!

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

モバイルアプリ Web App PostgreSQL モバイルアプリから見るとここから 先がインフラ層

Slide 50

Slide 50 text

アンチパターン2選 50

Slide 51

Slide 51 text

アンチパターン : UIとインフラの密結合 サンプルコードではよくある(?)やつ

Slide 52

Slide 52 text

インフラ層の変更に Widget が直接影響を受けてしまう 実質的には1つのインフラ(DB)にのみ対応 テストが書きにくい、書けない コード量が最小限になる デメリット : メリット : アンチパターン : UIとインフラの密結合 💡 プロトタイピングでは有効な面も(それでもおすすめはしません)

Slide 53

Slide 53 text

アンチパターン : 全知全能モデル MVCフレームワークや O/R Mapper を使うとなりがち

Slide 54

Slide 54 text

アンチパターン : 全知全能モデル 複数のレイヤーの関心事が含まれてしまう DBの都合でモデルに変更が入る、とか クラス定義が1つで済む デメリット : メリット : 💡 DTOに辛みを感じたらスニペット利用やコード生成などで 単純作業コストを減らそう。

Slide 55

Slide 55 text

まとめ 55

Slide 56

Slide 56 text

今日話したことまとめ インフラ移行は意外と起こる コードーベースの初期設計から疎結合に保つことが大事 Flutter/Dart でオニオン・アーキテクチャは手軽に実践可能! 56

Slide 57

Slide 57 text

結論

Slide 58

Slide 58 text

ありがとうございました! https://github.com/iktakahiro/flutter-starter https://github.com/iktakahiro https://iktakahiro.dev/ @iktakahiro サンプルコード 58