[Android] Architecture: The Domain Layer — MAD Skills(4/5)

Max Chiang
7 min readJun 5, 2022

--

這是介紹 Architecture 的第四篇。想看第三篇 UI Layer 的請服用:

The Domain Layer

Domain Layer 介於 UI Layer 跟 Data Layer 之間,視需求而使用,通常負責封裝商業邏輯,定義了事件觸發後、資料變動後要做的事情,例如「點擊按鈕後需要去取得最新的資料」,以及取得資料後,「因為特定情況需要顯示錯誤訊息」。

在比較小的 App 中,商業邏輯通常會直接寫在 UI Layer 或 Data Layer,但隨著 App 的成長,通常會將商業邏輯統一整理到 Domain Layer 中,將分工拆分得更為細緻。Domain Layer 不負責決定資料如何顯示(那是 UI Layer 的工作),也不負責獲取或是儲存資料(那是 Data Layer 的工作),Domain Layer 只負責處理 App 的商業邏輯。

UseCase

Domain Layer 由 Interactor 或 UseCase 組成

UseCase 命名

UseCase 代表用戶可以執行的單個任務, 而 UseCase 的命名該怎麼做呢?通常會是 動詞+名詞+UseCase,以 GetLatestNewsUseCase 為例,Get 是動詞、LatestNews 是名詞(對象),最後再加上 UseCase,陳述這個 UseCase 的主要用途是 取得最新的新聞資料

UseCase 可依賴 Repositories 及 UseCases,不能依賴 ViewModel

UseCase 可以依賴 lower layer(例如 Data Layer 的 Repositories),也可以依賴其他 UseCases,但不能依賴 higher layer(如 UI Layer 的 ViewModel)。以下面的例子,GetNewsWithAuthorsUseCase因為需要新聞與作者的資料,所以依賴於 NewsRepositoryAuthorsRepository ,而且還會需要將日期格式化,所以也依賴 FormatDateUseCase 來做到這件事情。

善用 invoke 來使用 UseCase

在 Kotlin 中,可以使用 operator 輔助鍵定義 invoke() 函式,即可將實例化的類別作為函數一樣調用。

上面的程式碼先定義了FormatDateUseCase 中的 invoke() 方法,因此在下面的 ViewModel 就可以將 FormatDateUseCase 的實例作為函數一樣調用。

生命週期

UseCase 要盡量保持簡單、輕量,並且要是不可變的。UseCase 沒有自己的 Lifecycle,而它們的作用範圍僅限於使用它們的類別。UseCase 不應該包含任何可變動的資料,所以每次要傳送資料時,都應該建立新的實例。也不要緩存任何 cache 在 UseCase 裡。

線程處理

Domain Layer 必須確保 main-safe。換句話說,需要讓所有的函式都能夠安全地從主執行緒被呼叫。如果需要執行長時間的任務,要記得將邏輯移至適當的執行緒。當然,也要記得檢查一下,說不定這些耗時的任務,其實更適合放在其他 Layer (如 Data Layer)進行。

適合使用 UseCase 的情境

  • 封裝可重複使用的商業邏輯

你可以將常用邏輯封裝到 UseCase 來增加重用性。例如 App 可能在很多地方都需要將日期格式化,多個 ViewModel 都會共用同樣的邏輯,你就可以寫一個 DateFormateUseCase 來共用 DateFormate 的方法。此外,你也可以寫一個 BaseUseCase 來做線程操作及錯誤處理,再讓其他 UseCase 來繼承。

  • 組合來自多個 repositories 的資料

有的時候必須透過多個 Repositories 才能組合出完整的資料,通常不會將這種複雜的組合邏輯放在 ViewModel,而會讓 UseCase 來處理。分工細緻的好處,就是可以將每個 unit 切小,降低複雜度也能夠提升可測性。

Data access restrictions

Domain Layer 介於 UI Layer 跟 Data Layer 之間

前面提到 Domain Layer 不是必要的,如果邏輯不會太複雜,其實不需要使用到 Domain Layer。不過一旦在 App Architecture 中引入了 Domain Layer,會需要限制 UI Layer 無法直接使用 Data Layer 嗎?

嚴格限制 UI Layer 無法直接使用 Data Layer 的好處在於,可以避免不小心繞過 Domain Layer 的一些邏輯。例如在取用特定資料時,如果有針對這些行為另外去做了 Log 紀錄的事情,通常是由 UseCase 來處理。假如 UI Layer 可以直接使用 Data Layer,那麼不知情的人可能就會漏掉 Domain Layer 的邏輯處理。強制透過 Domain Layer 的話,就不需去擔心是否有什麼流程被忽略了。

此外,如果強制需要使用 Domain Layer 的話,會因為 ViewModel 只有相依 UseCase,會讓 ViewModel 的測試更加單純。

不過凡事都得經過 Domain Layer 其實也會增加複雜性,畢竟有的時候並沒有額外的商業邏輯,Domain Layer 只是負責幫忙呼叫 Data Layer 的函式而已。是否需要限制 UI Layer 不能直接使用 Data Layer?嚴格限制有嚴格限制的好處,彈性也有彈性的好處,就是看團隊間的共識了。

Summary

Domain Layer 能封裝商業邏輯來降低 UI Layer 的複雜性,也能封裝多個 ViewModel 的共用邏輯來增加重用性。因為將邏輯拆分的更細小的單元,也可以提高可測試性。是否需要使用 Domain Layer 以及如何使用,可以在團隊間討論出最適合的做法再做決定。

--

--