Mobile app architecture diagrams differ from server-side ones — the client has UI layer, ViewModel, Domain, Data, Native Module mixing, offline sync, push pipeline. This is the PlantUML template for iOS / Android / Flutter / React Native architectures, plus offline-first design, push integration, monitoring, performance hot spots.
@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 }
@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 }
@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"
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 : ① create order (no network) UI -> LDB : ② write to local DB UI -> Outbox : ③ enqueue pending sync op UI --> User : ④ show order immediately (local)
note over Sync : background detects network restored Sync -> Outbox : ⑤ fetch pending Sync -> API : ⑥ POST /orders API --> Sync : ⑦ 200 + server_id Sync -> LDB : ⑧ update server_id, mark synced
note over Sync : server has updates SSE -> Sync : ⑨ Server-Sent Event Sync -> LDB : ⑩ apply remote change (merge) Sync -> UI : ⑪ notify UI to refresh
@enduml
Core idea: local is truth, server is sync target. App still works when network is down.
Conflict resolution:
LWW (Last Write Wins) — simple, but can lose updates
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 : ③ show notification / background data
== iOS == Server -> APNs : ④ POST /push {device_token, payload} APNs -> Device : ⑤ Push Device -> Device : ⑥ show or wake background
note over Device When app opens: - App → subscribe topic - Get token from FCM/APNs on launch - Send to app server for storage end note
Device -> Server : ⑦ register device token (HTTP) Server -> Server : ⑧ store in DB (user_id, token, platform)
@enduml
iOS background push:
content-available: 1 wakes the app
limit: must be silent push, no alert
Android push channels:
FCM not usable in China — use Xiaomi Push / Huawei Push / OPPO Push / vivo Push / Meizu Push
multi-channel needs SDK integration, or unified via Getui / Jiguang abstraction
participant "App" as App participant "Firebase Crashlytics" as Crashlytics participant "Firebase Performance" as Perf participant "Sentry" as Sentry participant "Datadog RUM" as DD
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 Each module: - independent Pod/Gradle target - independent tests - exposes API via protocol end note
@enduml
Benefits:
Independent compile — change user module, don’t rebuild order
Independent test — each module has its own unit tests
Reuse — Core modules shared across apps
Field foot-guns
JS Bridge janks UI — frequent Native Module calls, scroll drops frames. Use JSI / Turbo Modules for sync bridge, or batch calls.
Local DB schema migration — user upgrades app, local DB schema changes. Room has Migration class, Drift has MigrationStrategy, must write migration tests.
Token expiry concurrent refresh — two APIs return 401 simultaneously, trigger two refreshes, second refresh token becomes invalid. Singleton refresh manager + mutex.
Push permission denied — after iOS user denies first time, must guide them to Settings. Don’t pop up again, use UIAlertController to guide.
Long list janks — RecyclerView/UITableView doesn’t reuse views. ViewHolder pattern + lazy image loading.
Flutter Dart isolate jank — CPU-intensive task (image processing) blocks UI isolate. Use compute() to run on background isolate.
Team? ├─ iOS / Android specialists → native ├─ Single team maintaining both ends → Flutter / RN └─ Web team → RN / Capacitor
Minimum viable mobile architecture: MVC + Repository + local cache. Production-grade: Clean Architecture + DI + modular + offline-first + monitoring.
Remember: mobile architecture diagrams differ from server — UI/UX is core, network is supplementary. First make UX smooth, then add complex architecture. Architecture is for solving problems, not for pretty diagrams.