Slide 1

Slide 1 text

FlutterとAngularDartを DIとClean Architectureで いい感じにする

Slide 2

Slide 2 text

自己紹介 ● 名前:林 尚之(はやし たかゆき) ● Agile(XP)、TDD、DDDとかが好き ● 株式会社ユーザベース ○ SPEEDA日本事業 CTO ● twitter: @t_hyssh 宣伝:9月の「XP祭り 2018」で登壇します

Slide 3

Slide 3 text

Mobile & Web Mobileの画面 Webの画面 ボタンをクリックすると数字がカウントアップ Webのスタイルがあれなのはご愛嬌・・・

Slide 4

Slide 4 text

「ボタンがクリックされると数字がカウントアップ」 という処理 これをFlutterとAngularDartで共通化してみる

Slide 5

Slide 5 text

「ボタンがクリックされると数字がカウントアップ」 という処理 ここから共通化可能なドメインロジックを抽出

Slide 6

Slide 6 text

「ボタンがクリックされると数字がカ ウントアップ」 ボタンをクリックするという処理は現状では完全 にUI側の処理なので除外

Slide 7

Slide 7 text

「数字がカウントアップ」 もうちょっといい表現にしてみる

Slide 8

Slide 8 text

「現在のカウントをインクリメントする」 いい感じ

Slide 9

Slide 9 text

「現在のカウントをインクリメントする」 この処理をAngularDartとFlutterで共通化する ためには・・・

Slide 10

Slide 10 text

Clean Architectureの出番

Slide 11

Slide 11 text

Clean Architectureの出番 Clean Architectureに則れば UseCaseとEntity(Domain Model)は外部リソースや UIに依 存しない。 ポイントは「画面の状態」保存先 (実際はインメモリ)を一番外側に 持っていく事。その状態を Gatewayを介して取得、保存す る。Gatewayの実装が AngularDartとFlutterで独自の実 装になり、実装は DIされる。

Slide 12

Slide 12 text

「現在のカウントをインクリメントする」 これをClean Architectureに合わせて再度修正

Slide 13

Slide 13 text

「現在のカウントを取得し、 インクリメントして保存する」 あとはこれをDartのコードにするだけ

Slide 14

Slide 14 text

Use Case

Slide 15

Slide 15 text

Domain イミュータブル好きなのでとりあえずイミュータブル

Slide 16

Slide 16 text

Port(Use Caseレイヤー) ● PortはUse Caseレイヤーに存在する仕様 ● ドメインの取得や保存の仕様を定義 ● このPortの仕様を実装したGatewayがAngularDartとFlutterそ れぞれに存在 ● 上記GatewayをDIする 依存関係逆転の原則( DIP)

Slide 17

Slide 17 text

Gateway(Flutter) ドメインの情報を基に状態を保存 (保存先はCountViewModelで、こいつを表示側が参照する。詳細は後ほど)

Slide 18

Slide 18 text

Gateway(AngularDart) ドメインの情報を基に状態を保存 (保存先はCountViewStateで、こいつを表示側が参照する。詳細は後ほど)

Slide 19

Slide 19 text

次はこの「状態」を AngularDartとFlutterでどうやって 表示するのか

Slide 20

Slide 20 text

FlutterはScoped Model AngularDartはChange Detection

Slide 21

Slide 21 text

Scoped Model(Flutter) ● Googleが開発しているマイクロカーネルの OSであるfuchsiaのUI(Flutter)にて採用されている 状態管理の考え、アーキテクチャー ● 親Widgetから子孫のWidgetに簡単にModelを渡す事が出来るようになる ● Modelの更新時にModelを使用する全ての子 Widgetを再レンダリング ● StatefullWidgetとStateを使用しなくてよくなる ○ その代わりScopedModelDescendantを多用する ● 詳細は https://github.com/brianegan/scoped_model

Slide 22

Slide 22 text

Scoped Modelの使用例

Slide 23

Slide 23 text

Scoped Modelの使用例(Model)

Slide 24

Slide 24 text

Scoped Modelの使用例(Widget) ここでScopedModelを使うとMaterialApp の子孫WidgetにてCountModelを参照し やすくなる

Slide 25

Slide 25 text

Scoped Modelの使用例(Widget) ScopedModelDescendantを使う事で builderの第3引数にmodelが渡される DI(というかService Locator)から UseCaseを取得しドメインロジックを実行

Slide 26

Slide 26 text

Change Detection(AngularDart) ● 詳細はlacoさんのブログを見てもらうのが一番(笑) ○ https://blog.lacolaco.net/post/translation-angular-2-change-detection-explained/ ● ざっくりいうと ○ Componentの状態や参照しているオブジェクトの状態の変更を検知して再レンダリングしてくれる ○ なのでComponentが参照するオブジェクトを DIの管理下に置いて、前述の Gateway側で更新をすると勝手に 画面が更新される

Slide 27

Slide 27 text

Slide 28

Slide 28 text

AngularDartの例(Component) CountStateは単なるDartのクラス。DIの管 理下に置くための設定は必要

Slide 29

Slide 29 text

これらの考えを基にしつつモジュール化 などをすれば画面側のクラスは状態の 参照とUseCaseの実行(イベントの発 火)のみにする事が可能

Slide 30

Slide 30 text

AngularDartはDIがビルトインされてい るがFlutterはDIは外部ライブラリーが必 要 自分はGoogleから出てるのを使ってます https://github.com/google/inject.dart

Slide 31

Slide 31 text

Scoped Model(Flutter)、Change Detection(AngularDart)とDIで下記のよ うな単方向アーキテクチャーに Component(Widget) UseCase Gateway State(Model) *矢印は依存関係ではなく、データの流 れ 変更の通知

Slide 32

Slide 32 text

レイヤー詳細 Component(Widget) UseCase Gateway Model(State) Domain Component(Widget)レイヤー Gatewayレイヤー Domainレイヤー UseCaseレイヤー <> Port

Slide 33

Slide 33 text

まとめ ● DIとScoped Model、依存関係逆転の原則を適用する事でAngularDartとFlutterの間で自然な 形でコードの共通化が可能 ● UI(Flutter、AngularDart)に依存しなくなるのでUnit Testも書きやすくなる ● 非同期にしたりStream使ってReactiveにするのも簡単 ○ なぜなら単なるDartのコードだから ● 「ここまでして共通化する必要あるの?」と問われれば 「モバイルPWAとアプリで全く同じ挙動にしたいという仕様でなければ共通化は逆に足かせに なるんじゃないでしょうか」と答えますw ● ただし、Clean Architectureに則れば保守性、拡張性の高いコードにしやすいと思うので共通 化しなくてもチャレンジする価値はあると思います