昨天從零開始設計了 Grimo 的專案架構,今天要來定義更細節的架構規範。
我站在白板前思考。
我有 10 年的後端開發經驗,熟悉六角架構(Hexagonal Architecture),但 Kotlin Multiplatform 是個新戰場。
我該堅持用熟悉的六角架構?跟隨 Android 社群的 Clean Architecture?還是有更適合 KMP + MVI 的方案?
於是,我決定找個資深顧問。AI。
我的第一個問題很簡單:
我:研究一下 Clean Architecture with MVI pattern 或是六角架構
哪一種更適合 Kotlin Multiplatform 專案?
AI 的回答讓我驚訝,它不只是比較兩個架構,而是給出了具體的評估維度。
MVI 整合度、生態系統支援、學習曲線、測試性、靈活性。
更重要的是,AI 給出了具體的程式碼範例:
// Clean Architecture + MVI 範例
class ProjectListViewModel(
private val getProjectsUseCase: GetProjectsUseCase
) {
fun processIntent(intent: ProjectListIntent) {
when(intent) {
is LoadProjects -> loadProjects()
}
}
}
// 六角架構 + MVI 範例
class ProjectListAdapter : ViewModel() {
private val projectService: ProjectService // Input Port
fun handleIntent(intent: ProjectIntent) {
// ...
}
}
看到程式碼,我立刻理解了兩種架構在 MVI 整合上的差異。
但我需要更多資訊,所以我追問:
我:在 KMP 專案中,如果我選擇 Clean Architecture,
shared module 和 platform-specific module 該如何劃分職責?
AI 不只回答了模組劃分,還給出了目錄結構:
shared/
├── commonMain/
│ ├── domain/ # 純業務邏輯
│ ├── data/ # 資料層介面
│ └── core/ # 共用工具
└── desktopMain/
└── data/ # 平台特定實作
desktopApp/
└── presentation/ # UI 層(Compose)
這讓我意識到,AI 不只懂理論,它還能結合實際專案結構。
接下來,我開始帶入自己的經驗:
我:我過去用六角架構處理複雜的金融系統,
Port 和 Adapter 的概念很清晰。
但在 KMP + MVI 中,這樣會不會過度設計?
AI 的回答很中肯。
對於 KMP + MVI,六角架構確實可能過度設計。MVI 本身已經定義了清晰的邊界。KMP 的模組系統已經提供了隔離。過多的抽象層會增加複雜度。
但你可以借用六角架構的精神。保持業務邏輯獨立、使用介面隔離外部依賴,但不需要完整的 Port/Adapter 結構。
這個回答結合了理論和實務,讓我找到了方向。
我先整理了專案需求給 AI。
專案:Grimo - KMP 桌面 AI 助理
技術棧:
- Kotlin 2.0 + Compose Multiplatform
- SQLDelight 資料庫
- 目標平台:macOS(未來擴展到 Android/iOS)
需求:
- MVI 單向資料流
- 支援離線優先
- 模組化架構
我請 AI 做了比較分析。
Clean Architecture + MVI 在 KMP 生態有廣泛支援,與 MVI 天然契合,學習成本中等,靈活性良好。Android 社群資源豐富,Intent 轉 UseCase 很直觀。
六角架構 + MVI 的範例較少,需要適配,學習成本較高,但靈活性極佳。
初期不需要太大靈活性,這是我的考量。
我要求 AI 給出具體的實作範例:
我:給我一個完整的 Clean Architecture + MVI 在 KMP 中的實作範例,
包含 Repository Pattern 和錯誤處理
AI 提供了完整的程式碼:
// Domain Layer - 純 Kotlin,無平台依賴
interface ProjectRepository {
suspend fun getAllProjects(): Result<List<Project>>
}
// Data Layer - 平台特定實作
expect class ProjectRepositoryImpl : ProjectRepository
// Desktop 實作
actual class ProjectRepositoryImpl(
private val database: GrimoDatabase
) : ProjectRepository {
override suspend fun getAllProjects(): Result<List<Project>> =
runCatching {
database.projectQueries
.selectAll()
.executeAsList()
.map { it.toDomain() }
}
}
// Presentation Layer - MVI
class ProjectListViewModel(
private val repository: ProjectRepository
) : ViewModel() {
private val _state = MutableStateFlow(ProjectListState())
val state = _state.asStateFlow()
fun handleIntent(intent: ProjectIntent) {
when(intent) {
is ProjectIntent.LoadProjects -> loadProjects()
}
}
private fun loadProjects() {
viewModelScope.launch {
repository.getAllProjects()
.fold(
onSuccess = { projects ->
_state.update {
it.copy(projects = projects)
}
},
onFailure = { error ->
_state.update {
it.copy(error = error.message)
}
}
)
}
}
}
這個範例讓我看到了完整的資料流和錯誤處理。
六角架構的優點是業務邏輯完全獨立、容易替換技術細節、測試非常容易。
但過去的痛點也很明顯。小專案用六角架構太重、新人上手困難、過多的介面和轉換。
KMP 生態現況是 Clean Architecture 有大量範例、官方範例也採用類似架構、工具鏈支援更好。
MVI 的特性已經提供了清晰的單向資料流、Intent 自然對應到 Use Case、State 管理已經很完善。
結合兩者,我做出了決定:
// 採用 Clean Architecture 的分層
// 但簡化 Use Case 層(只在複雜業務邏輯時使用)
// 簡單情況:直接使用 Repository
class SimpleViewModel(
private val repository: ProjectRepository
) : ViewModel() {
fun loadProjects() {
// 直接調用 repository
}
}
// 複雜情況:加入 Use Case
class ComplexViewModel(
private val calculateMetricsUseCase: CalculateMetricsUseCase
) : ViewModel() {
fun calculateMetrics() {
// 複雜業務邏輯在 Use Case 中
}
}
不好的問法是「哪個架構比較好?」
好的問法是「我在開發 KMP 桌面應用,使用 SQLDelight 和 Compose,需要支援離線優先,Clean Architecture 和六角架構哪個更適合?」
不好的問法是「怎麼實作 Repository Pattern?」
好的問法是「在 KMP 中使用 SQLDelight,如何實作 Repository Pattern?請包含錯誤處理和 expect/actual 的使用」
我會用三個方法驗證。
小範圍實作,先寫一個簡單功能驗證。查找官方文件,確認 AI 建議符合官方最佳實踐。社群確認,在 Kotlin Slack 或論壇詢問。
我使用 ADR(Architecture Decision Records)記錄:
# 使用 Clean Architecture 搭配 MVI
## 背景
評估了 Clean Architecture 和六角架構
## 決策
選擇 Clean Architecture
## 理由
1. KMP 生態支援更好
2. 與 MVI 自然契合
3. 學習資源豐富
## 結果
- 清晰的分層架構
- 良好的測試性
- 但需要注意不要過度設計
決策效率大幅提升。
過去需要花幾天研究文件、寫 POC,現在幾小時內就能完成。從漫無目的的搜尋到有針對性的對話。快速驗證想法的可行性。
品質也提升了。
AI 提醒了我沒考慮到的點(如 KMP 的 expect/actual)。獲得了最新的最佳實踐(2024 年的)。避免了過度設計。
信心增加。
有具體程式碼可以參考。決策有明確的理由支撐。可以快速驗證想法。
AI 的程式碼可能過時。
// AI 給的(過時)
import kotlinx.coroutines.experimental.*
// 實際應該用
import kotlinx.coroutines.*
解決方法?永遠檢查版本號。
AI 可能過度複雜化。AI 有時會給出「教科書」式的完美架構,但實際專案不需要這麼複雜。
解決方法?明確告訴 AI 你要「簡單實用」的方案。
AI 不了解你的具體情況。不知道你的團隊大小、時程壓力、技術債。
解決方法?提供完整背景。
提出問題 → 獲得多個選項 → 要求比較分析 → 深入優缺點
範例對話:
我:KMP 專案中處理資料持久化有哪些選擇?
AI:[列出 SQLDelight, Room, Realm 等]
我:比較 SQLDelight 和 Room 在 KMP 中的優缺點
AI:[詳細比較表]
要求程式碼範例 → 本地實作測試 → 發現問題 → 請 AI 解決 → 回到測試
整理所有資訊 → 做出決策 → 寫 ADR 文件 → 實作
讓我分享一個實際的例子。
我:KMP Desktop 專案要選擇資料庫,有什麼建議?
AI:建議考慮:
1. SQLDelight - 類型安全,KMP 原生支援
2. Exposed - JetBrains 的 ORM
3. H2 Database - 輕量級 Java 資料庫
我:我需要支援未來擴展到 iOS/Android
AI:那麼 SQLDelight 是最佳選擇,因為:
- 真正的 Multiplatform 支援
- 編譯時期 SQL 驗證
- 自動產生 Kotlin 程式碼
我:如何處理資料庫遷移?
AI:[提供完整的 migration 策略和程式碼]
我:有什麼 SQLDelight 的最佳實踐要注意?
AI:
1. 使用 Flow 監聽資料變化
2. Transaction 的正確使用
3. 避免 N+1 查詢問題
[附上程式碼範例]
最終,我在 2 小時內就確定了資料庫方案,而且有完整的實作指引。
AI 能幫你什麼?
快速獲得選項,不用花時間 Google。比較分析,客觀的優缺點評估。程式碼範例,立即可用的實作。最新資訊,2024 年的最佳實踐。
AI 不能幫你什麼?
理解你的業務,只有你知道產品需求。評估團隊能力,你知道團隊的強弱項。做最終決策,責任在你。
我的心得?
AI 就像一個經驗豐富但不了解你公司的顧問。它能給你選項和建議,但整合到你的情況中,需要你的判斷。
最重要的是,不要盲信,要驗證。
每個 AI 的建議都要寫程式碼驗證、查官方文件確認、考慮實際情況調整。
記住,AI 讓你站在巨人的肩膀上,但路還是要自己走。
「最好的架構不是最完美的,而是最適合團隊現況的。」
關於作者:Sam,一人公司創辦人。正在打造 Grimo,一個智能任務管理和分配平台。
專案連結:GitHub - grimostudio