Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Mobileアプリのアーキテクチャ設計法
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Keita Kagurazaka
April 21, 2022
Programming
1.5k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Mobileアプリのアーキテクチャ設計法
2022/04/21 (木) に開催されたKyash Tech Talk #3の登壇資料です。
Keita Kagurazaka
April 21, 2022
More Decks by Keita Kagurazaka
See All by Keita Kagurazaka
三者三様 宣言的UI
kkagurazaka
0
570
SELECT FOR UPDATEの話
kkagurazaka
0
530
原理から完全理解するDagger Hilt Migration
kkagurazaka
1
2k
今後のJetpackでAndroid開発はこう変わる!
kkagurazaka
16
6.5k
外部SDKのViewにマスク処理をする方法と罠
kkagurazaka
0
1.1k
AWAのフルリニューアルを支えたアーキテクチャ
kkagurazaka
1
980
CQRS Architecture on Android
kkagurazaka
7
3.2k
suspending functionの裏側
kkagurazaka
3
490
coroutinesで非同期ページネーション
kkagurazaka
1
720
Other Decks in Programming
See All in Programming
そのリトライ、死んだコネクションを使い回していませんか ── GoのHTTPクライアントとHTTP/2を実プロダクト障害から学び直す
myus4a
0
160
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
260
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
520
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
0
1.5k
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
2k
Agents on Rails - Rails at Scale 2026
irinanazarova
0
110
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
120
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
1
300
JRuby: Past, Present, and Future
headius
0
160
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
4
2.9k
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
650
AI × TiDD / 2026.09.05 Redmine 大阪
tokudiro
1
180
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
280
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
67
58k
The Invisible Side of Design
smashingmag
301
52k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
250
Raft: Consensus for Rubyists
vanstee
142
7.7k
Site-Speed That Sticks
csswizardry
13
1.5k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
Making Projects Easy
brettharned
120
6.8k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
940
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
440
Transcript
Mobileアプリの アーキテクチャ設計法 〜KMMを例に〜 Keita Kagurazaka
課題と制約
課題と制約 • 現状と理想のギャップが課題 • 選択肢を制限するものが制約 ◦ 技術的なもの ◦ チーム由来のもの ◦
歴史的経緯によるもの • 制約を守りつつ、課題を解決しなければならない • アーキテクチャは課題の解決方法の1つ
Kyashでの課題
Kyashでの課題 • [課題1] 両OSで同じロジックを2度実装することによる問題 ◦ リソースの無駄遣い ◦ 同メンバーが対応することによる虚無感 (特にテスト) •
[課題2] OSでロジックが異なったことによるインシデント ◦ 画面遷移可能かの判断で非常に複雑な場所があった ◦ その判定ロジックがOS間で異なり、片方のOSでだけインシデントが発 生
制約条件 • [制約1] 各OSごとにUIを最適化し、UXを最大化する方針 • [制約2] フルリニューアルを実施することができない ◦ 事業の状態を考えたときのリソースに起因 ◦
すなわち、部分的な導入がしやすく、それによって既存部分に与える 影響を最小化したい
制約条件 • [制約1] 各OSごとにUIを最適化し、UXを最大化する方針 • [制約2] フルリニューアルを実施することができない ◦ 事業の状態を考えたときのリソースに起因 ◦
すなわち、部分的な導入がしやすく、それによって既存部分に与える 影響を最小化したい [選択1] Kotlin Multiplatform Mobile
共通化するレイヤー • KMMは部分導入の自由度が高い ◦ APIクライアントだけ ◦ UseCaseまで ◦ etc.
共通化するレイヤー • KMMは部分導入の自由度が高い ◦ APIクライアントだけ ◦ UseCaseまで ◦ etc. •
[課題1] 両OSで同じロジックを2度実装することによる問題 の解決のため には [選択2] UI以外は原則すべて共通化する
選択による課題と制約
KMM採用の課題と制約 • [制約3] iOSメンバーはKotlinの経験がなく、これから学ぶ必要あり • [制約4] 過去のPRにしかないストック情報がある ◦ [選択3] KMMのリポジトリを分ける
◦ アプリ側のリポジトリとのインテグレーションが必要 ◦ [課題3] インテグレーションのオーバーヘッド • [課題4] Kotlin Nativeにおけるmulti-thread問題 ◦ 以下native-mt問題
native-mt問題 • Kotlin Nativeでは原則としてオブジェクトがスレッドを跨げない • 例外はfreezeしたfrozen object ◦ frozen objectを変更するとランタイムでクラッシュ
• 基本的な対応策 ◦ スレッドを跨ぐオブジェクトはImmutableにする ◦ mutable stateを持つクラスがfreezeされないようにする
native-mt問題 • [制約3] iOSメンバーはKotlinの経験がなく、これから学ぶ必要あり ◦ 注意して開発しよう、は選択できない • 課題を制約に変換して解消 ◦ [制約5]
frozenをアーキテクチャ基盤に隠蔽し、ロジックやUIの実装時 に意識させない
アーキテクチャの選定 • [選択4] MVIベースのアーキテクチャを採用する • ImmutableなIntentをUI側から入力し、ImmutableなUI state / eventを 出力してもらう
• 各レイヤーで受け渡されるデータがすべてImmutableであれば、基盤側 でfreezeしちゃえばnative-mt問題は発生しない
UI層の技術選定 • [選択5] UI toolkitには宣言的UIを採用 • [制約1] 各OSごとにUIを最適化し、UXを最大化する方針 ◦ OS標準のUI
toolkitを選択したい • [選択4] MVIベースのアーキテクチャを採用する ◦ ImmutableなUI stateが差分ではなく全体として更新通知される
アーキテクチャ概観 UI StateHolder UseCase Repository ApiClient state Intent event
アーキテクチャ概観 UI StateHolder UseCase Repository ApiClient state Intent event Intent
→ UI state or event
アーキテクチャ概観 UI StateHolder UseCase Repository ApiClient state Intent event
アーキテクチャ概観 UI StateHolder UseCase Repository ApiClient state Intent event [課題5]
IntentがAndroidの既 存概念とコンフリクトする
アーキテクチャ概観 UI StateHolder UseCase Repository ApiClient state Action event [選択6]
UI層からの入力は Actionと命名
自動テスト戦略 • [課題2] OSでロジックが異なったことによるインシデント
自動テスト戦略 • [課題2] OSでロジックが異なったことによるインシデント • [選択7] 自動結合テスト ◦ HttpClientにおけるレスポンスのみをmockする ▪
KMMではKtor clientのMockEngineを活用 ◦ 全Actionに対し、期待となるUI state / eventを検証 ◦ expect / actualを活用し、JVM / iOS両方の環境でのテストをワン ソースで実施
テストコードのイメージ @Test fun executeAction_loadInitially_success() = reactorSuspendTest( reactorFactory = ::CouponListReactorFactory, responses
= { every { "/v1/coupons/list?limit=10" } returns SUCCESS_RESPONSE_1_3 } ) { reactor -> reactor.test { reactor.execute(Action.LoadInitially) assertTrue(awaitState() is LoadState.Loading) /* 省略 */ } }
テストコードのイメージ @Test fun executeAction_loadInitially_success() = reactorSuspendTest( reactorFactory = ::CouponListReactorFactory, responses
= { every { "/v1/coupons/list?limit=10" } returns SUCCESS_RESPONSE_1_3 } ) { reactor -> reactor.test { reactor.execute(Action.LoadInitially) assertTrue(awaitState() is LoadState.Loading) /* 省略 */ } }
テストコードのイメージ @Test fun executeAction_loadInitially_success() = reactorSuspendTest( reactorFactory = ::CouponListReactorFactory, responses
= { every { "/v1/coupons/list?limit=10" } returns SUCCESS_RESPONSE_1_3 } ) { reactor -> reactor.test { reactor.execute(Action.LoadInitially) assertTrue(awaitState() is LoadState.Loading) /* 省略 */ } }
テストコードのイメージ @Test fun executeAction_loadInitially_success() = reactorSuspendTest( reactorFactory = ::CouponListReactorFactory, responses
= { every { "/v1/coupons/list?limit=10" } returns SUCCESS_RESPONSE_1_3 } ) { reactor -> reactor.test { reactor.execute(Action.LoadInitially) assertTrue(awaitState() is LoadState.Loading) /* 省略 */ } }
CIによるインテグレーション省力化 • [課題3] インテグレーションのオーバーヘッド • [選択8] 徹底的なインテグレーションの自動化 ◦ maven repository
/ XCFrameworkを置く成果物リポジトリ ◦ KMM側のPRに対応して成果物リポジトリにbranchが生える ◦ SNAPSHOT運用されるので、そのbranchをrefする ◦ KMM側のPRが閉じたら成果物リポジトリのbranchも消える ◦ その他tagを切って過去バージョンをアーカイブしたり、QA期間用のフ ローも自動化
PoC実装
Cache strategy • [課題6] Cache DBの変更通知をUI stateに反映するフローを自然に表現 できない Model User
Action state fun update( prev: State a: Action ): State Server
Cache strategy • [課題6] Cache DBの変更通知をUI stateに反映するフローを自然に表現 できない Server DB
? Model User Action state fun update( prev: State a: Action ): State
Cache strategy • [課題6] Cache DBの変更通知をUI stateに反映するフローを自然に表現 できない Server DB
Model User Action state fun update( prev: State a: Action ): State
Cache strategy • [課題6] Cache DBの変更通知をUI stateに反映するフローを自然に表現 できない Server DB
Model ? state fun update( prev: State a: Action ): State User Action
調査 • KMM + MVIで調査したところ、Wantedlyさんの記事を発見 • https://www.wantedly.com/companies/wantedly/post_articles/300999 ◦ 2020年のKotlin Advent
Calendar最終日の記事
ReactorKitから借用する • [選択9] Action -> UI stateの変換を2 stepsに分ける Model User
Action state fun mutate( a: Action ): Mutation fun reduce( prev: State, m: Mutation ): State Mutation Server DB
結果 • スタートポイントの課題は解決 ◦ これまでより品質が担保できる状態 • iOSメンバーの感想 ◦ Kotlinの書き方は学ぶ必要があったが、アーキテクチャやiOSだから という理由で困ったことはなかった
• 単純にコード量が減ったというだけでなく、書ける人・レビューできる人がこ れまでより増えた面も
Why not • 既存のアーキテクチャフレームワークを使わず、自前で基盤を実装 • 解くべき課題と制約は事業・チームが異なれば変わる • 何なら時間でも変化する
native-mt問題の完全解決 • Kotlin NativeにおけるNew Memory Manager ◦ インスタンスがスレッドを跨げないという制約がなくなる ◦ まだexperimentalでnot
production ready • これからKMMにtryしたい人は、これがreadyになるのを待ちましょう • 非常に大きな制約が消えるので、必然的にアーキテクチャ設計に影響する でしょう
まとめ • 世のアーキテクチャがシンプル or オーバーエンジニアリングに見えるの は、解くべき課題や制約がそれぞれで異なるから • 課題を解決するアイデアとしてのアーキテクチャの知識は必要 • 事業やチームの課題と制約を明確に意識して設計しましょう
アーキテクチャは 選定するのではなく 設計せよ
Thanks!