架構圖主要分成幾個區塊,綠色是 Model 的部分,所有的商業邏輯都集中在此,這個部分原則上盡可能是 platform-independent 的設計,因此以 Java SE 和 Android SDK 交集的 API 完成,在自動化測試時,完全不需要 Android 模擬器,用一般 JUnit 即可,希望用最少時間達到最大的測試涵蓋率,過去曾經統計過,核心功能部分的程式碼約是二萬八千多行,由三萬二千多行的測試程式,約一千五百個測試案例驗證功能的正確性,全部測時能在 40 秒內完成,測試涵蓋率在 95% 以上。
這邊要特別強調的是 Model,對於習慣只把資料結構當成 Model 的人來說,商業邏輯可能四散在 View、Controller (這裡的 Controller 指的是 MVC 中的 Controller) 或 Activity 中,但資料結構只是 Model 的一部分,也就是圖中 Domain Data Model 那一塊,維護資料結構物件關係的邏輯,如何回應 Use case 或 user story 所定義的系統事件 (system event),這些處理系統事件的 main controller 都屬於 Model,也就是圖中 Business Logic Managers,責任是透過 Web Service Interfaces 和伺服器溝通,然後維護資料結構物件間的關係,最後透過 DAO Interfaces 將狀態保存到行動裝置中。
Platform-dependent adapters
橘色是 platform-dependent 的實作,例如用 Android 的 SQLite API 來實作DAO,然後以 Setter Injection 或 Interface Injection 的方式,將 platform-dependent 的實作注入,為了做到這點,綠色區塊不能直接依賴 platform-dependent 的實作,因此 Web Service Interfaces 和 DAO Interfaces 兩個區塊只定義介面沒有實作,由橘色區塊中的 Web Service Implementation 和 DAO Implementation 分別實作這兩個介面,藉此將依賴關係反轉 (Dependency inversion),因此也容易把 Web Service Interfaces 和 DAO Interfaces 的 mock object 給注入,方便測試。
橘色區塊還有一個責任是扮演 Android Service 的角色,Android 能讓 App 以 Service 的方式在背景執行,以需要頻繁更新狀態的 App 類型來說,能有在背景執行的 Service 非常有用。當 Activity 被喚起,只需 bind service 就能取得所有最新的狀態。不過,設計不好,Service 是非常耗電的,若能以 GCM 喚起 App 進行單次的網路存取,也是一種用來更新狀態的一種方式,不一定得用 Service。此外,Service 與 Activity 共用同一個 thread,不能讓 Service 的初始化佔用太多時間,否則 App 啟動時,UI 會卡住無法動彈,以額外的 thread 進行初始化需要注意 multi-thread 引起的問題。
DAO Implementation 是 platform-dependent 的,所以用橘色標示沒什麼大問題,但 Web Service Implementation 卻是被標示為紫色,主要是因為以 JSON 格式傳遞資料的 Restful Web Services,是有機會只用以 Java SE 和 Android SDK 交集的 API (像是HttpURLConnection),或是是用 OkHttp,搭配 Android 上也能使用的 JSON Library (Gson 或 Jackson)來完成,也就是 Web Service Implementation 不一定是 platform-dependent 的實作,有機會能用 JUnit 來測試。
離開這專案後再回來想這個架構的優缺點,首先,為了讓自動化測試能不依賴 Android 模擬器,以 Java SE 和 Android SDK 交集的 API 完成有個缺點,要等到 Android SDK 正式支援 Java 8,才能在 Model 中開始使用 Java 8 的新特性 (像是 Lambda),不過,從 Android N 開始,已經開始支援大多數 Java 8 的特性,這缺點就沒這麼嚴重了。
跨平台共用
當初腦中確實有想過讓這 App 的核心能跨足到 PC 版,但跨平台不是最主要的考量,讓團隊成員能習慣從 domain 開始寫程式,才是這架構中 Model 與 UI 分開最主要的原因,不過後來真的開發 PC 版了,核心數萬行的程式不需重寫,想像一下,核心就像下圖中綠色的拼圖,無法單獨使用,需要其他的拼圖才能組成一個完整的 App,開發 PC 版時,則是提供藍色的拼圖整合核心的部分成為另外一個可以在 Mac 與 Windows 上執行的程式。
相依性管理
在跨平台時有一點要注意,這也是當初沒有做好的部分,當初為了開發方便,Android 相關的程式與核心的部分是同一個專案的二個模組,不像是引入第三方套件的方式引入核心到 Android 專案中,PC 版也是以 link 的方式直接將原始碼引入,但一旦核心被二個專案以原始碼形式引用時,任何一邊對核心的介面做了修改,都會導致另一邊編譯失敗,較理想的方式,應該是將核心的部分獨立出去,任何修改後建置成一個 JAR 檔並加上版本布署到內部的 repository 中,Android 與 PC 版的專案以第三方套件的方式引入,這樣便能夠以管理第三方套件版本的方式,讓雙方隨時有相容的介面可以編譯。