iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
生成式 AI

30 天一人公司的 AI 開發實戰系列 第 24

Day 24: 架構師切分邊界:shared vs desktopApp 的架構重構

  • 分享至 

  • xImage
  •  

前言:專案結構的困惑

最近在看 Grimo 專案時,我一直在思考一個問題。

shared 模組到底該放什麼?

看了一下 build.gradle.kts,shared 裡面有 compose 相關的依賴:

// shared/build.gradle.kts
dependencies {
    implementation(compose.runtime)
    implementation(compose.foundation)
    implementation(compose.material3)
    // ...
}

但實際的目錄結構裡,presentation 層都在 desktopApp:

shared/src/commonMain/
├── core/      # 核心工具
├── data/      # 資料層
├── di/        # 依賴注入
└── domain/    # 業務邏輯

desktopApp/src/jvmMain/
├── presentation/  # 所有 UI 都在這
├── event/        # Desktop 特定功能
└── Main.kt

這是對的嗎?我需要研究一下。

用 Claude Code 研究架構

請教 KMP 的模組劃分

「Claude Code,幫我研究一下 KMP 專案的 shared 和平台模組該如何劃分。」

Claude Code 開始分析:

「讓我看看你的專案結構...」它掃描了整個專案。

「你現在的架構其實是正確的,」Claude Code 解釋,「shared 只包含業務邏輯,UI 都在 desktopApp。但 shared 的 build.gradle.kts 有 compose 依賴,這可能是歷史遺留或未來考量。」

研究其他專案

「幫我看看其他 KMP 專案是怎麼做的。」

Claude Code 找了幾個參考:

  1. JetBrains 官方範例:UI 完全分離到平台模組
  2. Compose Multiplatform 範例:共享 UI 放在 shared(新趨勢)
  3. Square 的專案:嚴格分離,shared 只有業務邏輯

「看起來有兩種流派,」我總結,「一種是 UI 完全分離,一種是用 Compose Multiplatform 共享 UI。」

分析我的專案

檢查 shared 模組

我讓 Claude Code 檢查 shared 模組裡到底有沒有 UI:

find shared/src -name "*.kt" | xargs grep -l "@Composable" | head -5

結果是空的。shared 裡確實沒有任何 @Composable。

那為什麼 build.gradle.kts 有 compose 依賴?

挖掘原因

「可能的原因有哪些?」我問 Claude Code。

Claude Code 分析:

  1. 歷史遺留:之前可能有 UI 在 shared,後來移走了但依賴沒清理
  2. 未來預留:準備用 Compose Multiplatform 共享 UI
  3. 間接依賴:某些庫需要 compose.runtime

我檢查了一下 Decompose 的依賴:

// shared 有用 Decompose
implementation(libs.decompose)
implementation(libs.decompose.extensions.compose)  // 這個需要 compose

原來如此!Decompose 的 compose 擴展需要 compose 依賴。

優化決策

該不該移除 compose 依賴?

我用 Day 11 提到的 ARS(架構研究專家)思維來分析。

「Claude Code,從架構角度分析,shared 裡的 compose 依賴該不該移除?」

Claude Code 給出了詳細分析:

保留的理由

  • Decompose 需要它來管理導航
  • 未來可能共享某些 UI 組件
  • 不影響編譯時間(沒有實際 UI 程式碼)

移除的理由

  • 更清晰的架構邊界
  • 減少 shared 模組大小
  • 避免開發者誤放 UI 到 shared

我的決定:暫時保留,但要加註釋說明。

實作架構約定

建立架構守護

我請 Claude Code 幫我建立一個架構檢查腳本:

#!/bin/bash
# check-architecture.sh

echo "檢查架構規範..."

# 檢查 shared 不該有 @Composable
if grep -r "@Composable" shared/src/commonMain --include="*.kt"; then
    echo "❌ 錯誤:shared 模組不應包含 @Composable"
    exit 1
fi

# 檢查 presentation 都在 desktopApp
if [ ! -d "desktopApp/src/jvmMain/kotlin/*/presentation" ]; then
    echo "❌ 錯誤:presentation 層應該在 desktopApp"
    exit 1
fi

echo "✅ 架構檢查通過"

文件化架構決策

參考 Day 20 的 MADR,我建立了架構決策記錄:

# ADR-003: UI 層與業務邏輯分離

## 狀態
已採納

## 背景
KMP 專案需要明確的模組邊界

## 決策
- shared:只包含 domain、data、core
- desktopApp:包含所有 UI(presentation)
- compose 依賴:因 Decompose 需要而保留在 shared

## 結果
- ✅ 清晰的架構邊界
- ✅ 未來容易擴展到其他平台
- ⚠️ shared 有 compose 依賴但無 UI 程式碼

優化 build 設定

精簡 shared 依賴

雖然決定保留 compose,但可以精簡:

// shared/build.gradle.kts
val commonMain by getting {
    dependencies {
        // 只保留必要的 compose
        implementation(compose.runtime)  // Decompose 需要
        // 移除這些
        // implementation(compose.foundation)  
        // implementation(compose.material3)
        // implementation(compose.ui)
    }
}

Claude Code 幫我測試編譯:

./gradlew clean build

成功!Decompose 只需要 compose.runtime。

測量改善

我請 Claude Code 幫我測量優化前後的差異:

# 優化前
du -sh shared/build/libs/
3.2M

# 優化後
./gradlew clean build
du -sh shared/build/libs/
2.8M

減少了 400KB。不多,但是個進步。

建立開發規範

給團隊(未來的自己)的指引

我建立了 docs/architecture/module-guidelines.md

# 模組劃分指引

## shared 模組
✅ 可以放:
- Domain models (Project, Task, etc.)
- Use cases
- Repository interfaces
- Core utilities

❌ 不要放:
- @Composable 函數
- ViewModels
- UI 相關程式碼

## desktopApp 模組
✅ 應該放:
- 所有 @Composable
- ViewModels
- Theme/Colors/Typography
- Platform-specific code

驗證架構的好處

新增功能時的體驗

最近要加一個新的任務詳情畫面,架構分離讓開發很順暢:

  1. Domain 層(shared):定義 TaskDetail 模型
  2. Data 層(shared):實作 TaskRepository
  3. UI 層(desktopApp):建立 TaskDetailScreen

每一層都很清楚自己的職責。

未來擴展的可能

如果要支援 Android:

androidApp/
└── src/main/kotlin/
    └── presentation/  # Android 特定 UI
        ├── theme/     # Material You
        └── screens/   # Android Composables

shared 完全不用動,這就是好架構的威力。

實戰心得

Claude Code 的幫助

這次架構研究,Claude Code 幫了很多忙:

  1. 快速掃描專案結構:找出潛在問題
  2. 研究最佳實踐:提供多個參考案例
  3. 分析利弊:幫助做架構決策
  4. 自動化檢查:建立架構守護腳本

但最重要的是,它讓我學會了架構思考的方法。

架構不是一成不變

開始時我以為 shared 有 compose 依賴是錯的,研究後發現有其道理。

架構決策沒有絕對的對錯,要根據專案需求和發展階段來調整。

文件化很重要

寫下架構決策的原因,未來的自己(或團隊成員)會感謝你。

特別是那些「看起來怪怪的」決定,一定要記錄為什麼這樣做。

總結

今天深入研究了 KMP 的模組劃分,理解了 shared 和 desktopApp 的職責邊界。

雖然沒有大規模重構(架構已經是對的),但通過優化依賴、建立規範、文件化決策,讓專案架構更加清晰。

最大的收穫是學會了用 Claude Code 做架構研究。它像一個架構顧問,幫我分析問題、研究方案、驗證決策。

明天會繼續探索其他架構優化的可能性。

今日金句

「好的架構不是完美的架構,而是適合的架構。」

參考資源

關於作者:Sam,一人公司創辦人。正在打造 Grimo,智能任務管理平台。

專案連結GitHub - grimostudio


上一篇
Day 23: UI 設計師做特效:從星光到魔法陣的載入動畫旅程
系列文
30 天一人公司的 AI 開發實戰24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言