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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
4
2.7k
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
280
SONY CISC-NEWS NWS-1750 + NWB-225 フレームバッファの NetBSD/news68k ドライバ実装 / OSC2026Hiroshima
tsutsui
0
120
What We Talk About When We Talk About XP
m_seki
2
660
一人だけ、Kiroが静止する日
hideg
0
120
アクセシビリティから考える情報設計
high_g_engineer
0
380
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
290
スマート反転とウェブアクセシビリティ
camiha
0
210
thread_parallel_with_free-threaded_Python_and_NumPy.pdf
riku_sakamoto
0
350
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
140
WebMCP Challenge に星空観察アプリで参加した話
okajun35
0
180
[GoCon2026] When Goroutines Are Not Enough: Runtime Locality in High-Throughput Go
takehaya
6
2.3k
Featured
See All Featured
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
200
Discover your Explorer Soul
emna__ayadi
2
1.3k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.9k
Building Applications with DynamoDB
mza
96
7.2k
How STYLIGHT went responsive
nonsquared
100
6.3k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
330
Mobile First: as difficult as doing things right
swwweet
225
10k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
510
Making the Leap to Tech Lead
cromwellryan
135
10k
Design in an AI World
tapps
1
320
Making Projects Easy
brettharned
120
6.8k
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!