移动 App 架构图:iOS / Android / Flutter / React Native 模块划分

puml.online

移动 App 架构图跟服务端架构图差异很大——客户端有 UI 层、ViewModel、Domain、Data、Native Module 混合、离线同步、推送链路。这篇是 iOS / Android / Flutter / React Native 四种架构的 PlantUML 模板,以及离线优先设计、推送集成、性能热点。

四种移动架构全景

1
2
3
4
5
6
7
8
9
10
移动 App 架构
├── 原生双端
│ ├── iOS (Swift / SwiftUI)
│ └── Android (Kotlin / Compose)
├── 跨平台
│ ├── Flutter (Dart + Skia)
│ ├── React Native (JS + Bridge)
│ └── Kotlin Multiplatform / Swift on Server
└── Hybrid (Web 套壳)
└── Capacitor / Cordova

怎么选:看团队、看性能要求、看交付速度。

原生 iOS 架构(Clean Architecture + MVVM)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@startuml
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

title "iOS App Architecture (Clean + MVVM)"

package "Presentation Layer" {
component "SwiftUI Views" as views
component "ViewModels (ObservableObject)" as vms
component "Coordinators" as coord
}

package "Domain Layer" {
component "Use Cases" as uc
component "Entities" as entities
component "Repository Protocols" as repo_proto
}

package "Data Layer" {
component "Repository Impls" as repo_impl
component "Network (URLSession)" as network
component "Local DB (Core Data / SwiftData)" as localdb
component "Keychain" as kc
component "File Cache" as fc
}

package "Cross-cutting" {
component "Analytics" as analytics
component "Logger (OSLog)" as logger
component "Error Handler" as eh
}

views --> vms : "observes state"
coord --> views : "navigation"
vms --> uc : "call"
uc --> entities : "operate on"
uc --> repo_proto : "depend on protocol"
repo_impl ..|> repo_proto : "conform"
repo_impl --> network : "API calls"
repo_impl --> localdb : "persist"
repo_impl --> kc : "tokens"
repo_impl --> fc : "files"

vms --> analytics : "events"
vms --> logger : "logs"
uc --> eh : "throw"

@enduml

模块划分要点:

  • Presentation 只关心 UI
  • Domain 是纯 Swift,不依赖 UIKit/SwiftUI——可测试
  • Data 实现 Domain 定义的 protocol
  • Cross-cutting 横切关注点(日志/分析/异常)

原生 Android 架构(MVVM + Hilt + Room)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@startuml
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

title "Android App Architecture (MVVM + Hilt)"

package "UI Layer (Compose)" {
component "Composables" as composables
component "ViewModel" as vms
component "Navigation" as nav
}

package "Domain Layer" {
component "UseCase" as uc
component "Domain Model" as dm
}

package "Data Layer" {
component "Repository" as repo
component "Retrofit (API)" as api
component "Room (DB)" as room
component "DataStore (Prefs)" as ds
component "WorkManager (Background)" as wm
}

package "DI (Hilt)" {
component "Modules" as hilt
}

composables --> vms : "collectState"
nav --> composables
vms --> uc
uc --> dm
uc --> repo
repo --> api
repo --> room
repo --> ds
repo --> wm : "sync"

hilt ..> vms : "inject"
hilt ..> repo : "inject"
hilt ..> uc : "inject"

@enduml

Hilt 注入贯穿三层——测试时换 fake 实现

Flutter 跨平台架构(BLoC + Repository)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@startuml
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

title "Flutter App Architecture (BLoC)"

package "UI" {
component "Widgets" as w
component "Pages" as pages
}

package "BLoC (State Management)" {
component "BLoC" as bloc
component "Events" as events
component "States" as states
}

package "Domain" {
component "UseCases" as uc
component "Entities" as e
component "Repositories (abstract)" as repo_abs
}

package "Data" {
component "Repository Impl" as repo_impl
component "Dio (HTTP)" as dio
component "Drift (SQLite)" as drift
component "Secure Storage" as ss
component "Flutter Secure Storage" as fss
}

package "Platform Channels" {
component "MethodChannel" as mc
}

pages --> w
w --> bloc : "dispatch event"
bloc --> events : "handle"
bloc --> states : "emit"
pages --> states : "listen"

bloc --> uc
uc --> e
uc --> repo_abs
repo_impl ..|> repo_abs
repo_impl --> dio
repo_impl --> drift
repo_impl --> fss

dio ..> mc : "calls native\nfor custom features"

@enduml

Platform Channels——Flutter 调用原生代码(iOS/Android module)的桥。

React Native + Native Modules 混合架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@startuml
title "React Native + Native Modules Hybrid Architecture"

skinparam componentStyle rectangle

package "JavaScript Thread" {
component "React Components" as react
component "Redux Store" as redux
component "JS Business Logic" as js_logic
component "TS SDK (API client)" as sdk
}

package "Bridge (async)" {
component "JSON Serializer" as json_ser
component "Bridge" as bridge
}

package "Native Modules (iOS)" {
component "Camera Module" as ios_cam
component "Bluetooth Module" as ios_bt
component "Apple Pay" as ios_pay
}

package "Native Modules (Android)" {
component "Camera Module" as android_cam
component "Bluetooth Module" as android_bt
component "Google Pay" as android_pay
}

react --> redux : "useSelector"
react --> js_logic : "callbacks"
js_logic --> sdk
sdk ..> bridge : "HTTP"
js_logic ..> bridge : "Native call"
bridge <--> ios_cam
bridge <--> ios_bt
bridge <--> ios_pay
bridge <--> android_cam
bridge <--> android_bt
bridge <--> android_pay

@enduml

Bridge 性能瓶颈——频繁跨 bridge 调用会卡 UI。批量、异步、必要时用 JSI/Turbo Modules 替代 bridge

离线优先同步架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@startuml
title "Offline-First Sync Architecture"

actor "User" as User
participant "UI" as UI
participant "Local DB\n(SQLite/Room/Drift)" as LDB
participant "Sync Engine" as Sync
queue "Outbox Queue" as Outbox
participant "API" as API
queue "Server Event Stream" as SSE

User -> UI : ① 创建订单(无网络)
UI -> LDB : ② 写本地 DB
UI -> Outbox : ③ 入队待同步操作
UI --> User : ④ 立即显示订单(本地)

note over Sync : 后台检测网络恢复
Sync -> Outbox : ⑤ 取出待同步
Sync -> API : ⑥ POST /orders
API --> Sync : ⑦ 200 + server_id
Sync -> LDB : ⑧ 更新 server_id,标记 synced

note over Sync : 服务端有更新时
SSE -> Sync : ⑨ Server-Sent Event
Sync -> LDB : ⑩ 应用远端变更(merge)
Sync -> UI : ⑪ 通知 UI 刷新

@enduml

核心思想:本地是真相,服务端是同步对象网络断开时 app 仍能用

冲突解决:

  • LWW(Last Write Wins)——简单,但可能丢更新
  • CRDT——复杂,但无冲突
  • Operational Transform——Google Docs 用
  • 应用层合并——按业务规则

推送链路架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@startuml
title "Push Notification Architecture"

participant "App Server" as Server
participant "FCM (Firebase)" as FCM
participant "APNs (Apple)" as APNs
participant "Device" as Device

== Android ==
Server -> FCM : ① POST /messages {token, data, notification}
FCM -> Device : ② Push via Google Play Services
Device -> Device : ③ 显示通知 / 后台数据

== iOS ==
Server -> APNs : ④ POST /push {device_token, payload}
APNs -> Device : ⑤ Push
Device -> Device : ⑥ 显示或后台唤醒

note over Device
应用打开时:
- App → 订阅 topic
- 启动时从 FCM/APNs 拿 token
- 发送给 app server 存储
end note

Device -> Server : ⑦ 注册 device token (HTTP)
Server -> Server : ⑧ 存到 DB (user_id, token, platform)

@enduml

iOS 后台推送:

  • content-available: 1 唤醒 app
  • 限制:必须 silent push,不能有 alert

Android 推送通道:

  • 国内不能用 FCM——用小米推送 / 华为推送 / OPPO 推送 / vivo 推送 / 魅族推送
  • 多通道需要 SDK 集成,或统一用 个推 / 极光 抽象

性能监控架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@startuml
title "Mobile Performance Monitoring"

participant "App" as App
participant "Firebase Crashlytics" as Crashlytics
participant "Firebase Performance" as Perf
participant "Sentry" as Sentry
participant "Datadog RUM" as DD

App -> Crashlytics : 崩溃堆栈
App -> Sentry : 异常 + 面包屑
App -> Perf : 启动时间、网络延迟
App -> DD : 用户操作 trace

note right of App
关键埋点:
- 启动时间 (cold start)
- 首屏渲染 (TTI)
- 网络请求耗时 (p50/p95)
- FPS (滚动卡顿)
- 内存峰值
- 电池消耗
end note

@enduml

关键指标:

  • 冷启动时间 < 2 秒
  • 首屏交互(TTI) < 1.5 秒
  • 网络请求 p95 < 500 ms
  • 滚动 FPS > 55
  • 崩溃率 < 0.1%

模块化架构(原生 + 模块化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@startuml
title "Modular iOS/Android Architecture"

package "App Shell" {
component "App Target" as app
component "App Coordinator" as app_coord
}

package "Feature Modules" {
component "User Module" as user_mod
component "Order Module" as order_mod
component "Payment Module" as pay_mod
component "Profile Module" as prof_mod
}

package "Core Modules" {
component "NetworkKit" as nk
component "DesignKit" as dk
component "AnalyticsKit" as ak
component "StorageKit" as sk
}

package "Shared Models" {
component "Domain Models" as dm
}

app --> app_coord
app_coord --> user_mod
app_coord --> order_mod
app_coord --> pay_mod
app_coord --> prof_mod

user_mod --> nk
user_mod --> dk
user_mod --> ak
user_mod --> sk
user_mod --> dm

order_mod --> nk
order_mod --> dk
order_mod --> ak
order_mod --> dm

pay_mod --> nk
pay_mod --> dk
pay_mod --> ak

note right of pay_mod
每个 module:
- 独立 Pod/Gradle target
- 独立测试
- 通过 protocol 暴露 API
end note

@enduml

好处:

  • 独立编译——改 user module 不重编译 order module
  • 独立测试——每个 module 单独 unit test
  • 复用——Core module 多个 app 共享

实战踩坑

  • JS Bridge 卡 UI——频繁 Native Module 调用,UI 跟手滑动掉帧。用 JSI / Turbo Modules 同步桥,或批量调用。
  • 本地 DB schema 迁移——用户升级 app,本地 DB 结构变了。Room 有 Migration 类,Drift 有 MigrationStrategy,必须写迁移测试。
  • Token 过期并发刷新——两个 API 同时 401,触发两次刷新,后一次 refresh token 失效。单例 refresh manager + mutex
  • 推送权限被拒——iOS 第一次拒后,要引导用户去 Settings 打开。不是再弹窗请求,是用 UIAlertController 引导
  • 大列表卡顿——RecyclerView/UITableView 没复用 view。保证 view holder 模式 + 图片懒加载
  • Flutter Dart isolate 卡顿——CPU 密集任务(图像处理)阻塞 UI isolate。compute() 跑在 background isolate
  • 网络重试雪崩——后端故障时所有客户端同时重试。exponential backoff + jitter
  • 离线写冲突——两个设备同时改同一订单。LWW + 冲突日志,让用户手动合并

决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
做什么?
├─ 极致性能(抖音/微信级) → 原生 + 各端优化
├─ 快速交付 / 跨平台 → Flutter
├─ Web 团队转 App → React Native
├─ 老 Native 项目维护 → 保留原生,新模块用 KMP/Swift Package
└─ 内部工具/H5 → Capacitor 套壳

性能要求?
├─ 60fps 不让步 → 原生
├─ 接近原生 → Flutter
└─ 流畅度略让步 → RN / Hybrid

团队?
├─ iOS / Android 各有专人 → 原生
├─ 一团队维护两端 → Flutter / RN
└─ Web 团队 → RN / Capacitor

最小可行移动架构:MVC + Repository + 本地缓存。生产级:Clean Architecture + DI + 模块化 + 离线优先 + 监控。

记住:移动端架构图跟服务端不同——UI/UX 是核心,网络是补充先把 UX 流畅度做好,再加复杂架构。架构是为了解决问题,不是为了画好看的图。

  • 标题: 移动 App 架构图:iOS / Android / Flutter / React Native 模块划分
  • 作者: puml.online
  • 创建于 : 2026-07-30 17:45:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-mobile-app-arch/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。