---
# System prepended metadata

title: '[Software Design] 循環依賴（Circular Dependency）：API 呼叫時，而 Token 過期需要刷新時產生的循環依賴'
tags: [Software Design, Android]

---

# 定義  
當兩個或多個類別／模組互相依賴彼此，形成封閉的迴圈，導致系統無法正確初始化或執行。

# 問題  
- 程式無法編譯或執行  
- 初始化順序不明，可能導致錯誤  
- 結構耦合，難以維護、擴展

---

# 常見類型

1. 模組之間互相引用  
2. 類別彼此引用對方  
3. 函式之間彼此調用且互相遞迴

---

# 解決方式
1. **重構結構**  
   - 抽共用邏輯到第三方模組  
   - 改變依賴方向（例如：下層不依賴上層）
2. **延遲依賴（Lazy / Provider）**  
   - 使用 `Provider<T>` 或 `lazy` 等方式延遲注入  
   - 可有效打破建構期的循環
3. **使用介面或抽象類**  
   - 解耦依賴，透過 interface 隱藏具體實作  
4. **依賴注入（Dependency Injection）**  
   - 將依賴從外部提供，而不是內部自行建構

---

# 實務例子：API 呼叫時，而 Token 過期需要刷新時產生的循環依賴

## 原始依賴鏈

1. Repository 呼叫 ApiService  
2. ApiService 經由 Retrofit 使用 OkHttpClient  
3. OkHttpClient 裡有 AuthInterceptor（檢查 token）  
4. AuthInterceptor 呼叫 TokenRefresher  
5. TokenRefresher 呼叫 UserRepository.login()  
6. UserRepository 又呼叫 ApiService.login()  
7. 回到起點 → 形成循環依賴

---

## 問題點

- Interceptor 是同步執行，不能直接呼叫 suspend 函式或進行網路請求  
- AuthInterceptor 屬於網路層，但呼叫了資料層（UserRepository），層級不對  
- ApiService 與 Interceptor 綁定在 OkHttpClient 上，形成建構時的相互依賴

---

## 解決策略

1. **AuthInterceptor 單純附帶 Token，不做刷新邏輯**  
2. **TokenRefresher 使用獨立 Retrofit 實例來刷新 token，避開原本 ApiService**
3. **改用 OkHttp 的 Authenticator 處理 401，自動重新發送請求**
4. **在 safeApiCall 層級統一處理 401，集中處理錯誤與重試邏輯**

---

## 分層原則（Clean Architecture）

| 層        | 負責內容                | 範例                   |
|-----------|-------------------------|------------------------|
| Presentation | ViewModel / UI           | 呼叫 UseCase           |
| Domain       | UseCase + Interface      | TokenRefresher, AuthRepository |
| Data         | Repository + API + Interceptor | ApiService, UserRepository |
| Network      | 網路細節 (Retrofit, OkHttp) | AuthInterceptor, Authenticator |

---

# 實務應對：將問題轉移到 safeApiCall 中處理
由於循環依賴發生在 Interceptor 介於 Retrofit 與 Dagger 的構建階段之間，處理位置不是那麼容易。

為了解決這個問題，我選擇不在 Interceptor 層級處理 token 過期，而是在上層的 Repository 的位置新增 safeApiCall() 中統一處理錯誤碼（如 401）。

好處：
* 可避免 Interceptor 進行網路呼叫造成循環
* 避開 Retrofit/OkHttpClient 建構時的物件初始化衝突
* 可以在資料層處理 token 過期與重試，邏輯集中可控
* 更容易加入 retry、log、回報、fallback 等彈性行為

