近年來 iOS app 架構設計的心得

這一兩年來自己在做 iOS app ,每每做一個新的服務,就可以在做新的嘗試。過去也不斷跑不同語言(Python, Ruby, .NET, Web 系 …)的研討會、去吸收各個語言在服務架構的設計心法;有時也會聽到中國來的講者分享架構,參考價值也非常高。

因為做的服務的商業邏輯,不太像是檯面上看得到的頂級服務,像是 Facebook 等級那一類的,而是相對比較小型的服務,所以從使用情境上就不太需要一啟動就套用太複雜的架構方式。

從服務的可維護性、新進人員的容易進入狀況等考量,我個人還是偏好架構在官方提出的 MVC 上。搭配 web service 介接後,再配合其他架構思維。

接著會從宏觀到微觀的角度帶到我的設計方式。可能在部分情境會不太適合,日後我也會再繼續加強架構設計的知識。

宏觀

從宏觀角度看的大架構來說,目前的用法當然是使用 Model-View-Controller 的這個做法。

加入 RxSwift 的話,不是整套換成 MVVC ,而是把 View 和 Controller 的部分,加入 View Model 來協助處理邏輯,一方面達到瘦身的效果。

也就是說,原本是: Model-View-Controller ,進展成 Model-[View+ViewModel]-[Controller+ViewModel] 的這種感覺

Model

Model 的部分應該就會包含存取資源(web service, db 等等),以及從 View 或 View Controller 切出來、用來處理資料的商業邏輯。

這個部分雖然是自己透過不動重構,歸納出一個做法。最近發現比較可以描述的架構方式,就是洋蔥架構。

從 Domain Service Layer 往內做的事情,自己是歸納成 Model 會做的事情。

裡面我從 Domain 的最外到內部又粗略的歸納成三層: Service, Route 以及 I/O 。

  • Service 層: 是負責跟 Application 溝通的地方,也就是 MVC 中的 Controller 。這邊的介面設計用語(或可以說是命名)動詞和名詞都會符合該使用情境和領域用語,在使用的時候會更加幫助了解這個方法的用途。如果是以 Apple 工程師的話 (WWDC 2016, session 419, 00:41)來說的話,比較算是 “Local Reasoning” 的效果。
  • Route: 負責組合 request 用的路徑 (以及 endpoint) ,讓 service 層的物件知道要去哪裡要東西,這一層的工作份量會相對薄很多。
  • I/O: 這一層就是負責跟洋蔥架構裡的 Domain Model (或可以說是 local storage 或 web service)溝通的窗口。這個地方就會用到 Alamofire, Magical Record 這一類的 library ,我的做法也是會把這一類的第三方相依,鎖在這個地方、並不會在界面上暴露用了這一類第三方函式庫的痕跡。

在 I/O 這個地方,如果牽扯到 RxSwift 就比較麻煩(因為有 URLSession.rx 這玩意),在接口上就會有 RxSwift 的 Observer ,目前還在想怎麼解除這個介面上的相依。

橫向溝通

縱向的設計基本上就是採用分層架構,分離責任後,就乾淨許多了。那如果是不同業務邏輯的物件呢?

我是用「不同子系統」的思考方式去看待不同業務邏輯的物件,點子是從微服務 (Microservices) 這個設計方式來的。

簡單來說,如果用「子系統」的思維來思考的話,自己是覺得可以

  • 更加功能專一、同質性高的業務邏輯會更加集中、毫無相干的邏輯間的耦合度會更加降低。
  • 因為根據不同業務邏輯切開了,介面的設計更能表達這一區塊想要達成的目的、提供的價值是什麼東西。
  • 在切得更乾淨的情形之下,要抽換業務邏輯模組會更加方便,只要介面可對接,有滿足介面的實作要接上原本的模組都不太會有問題。

介面設計

上面講的很多東西的基礎都是建立在設計及建立介面上。以 iOS 來說,就是 Protocol 這個東西,再搭配泛型的話,可以有更大的效果。

如果有實作好介面設計,在確保程式品質能夠達到很大的效果。在不同類別之間介接的時候,如果能夠做到只透過 protocol 溝通,在測試上可以幫助輕易的替換上 mock object ;在需求或實作方式改變的時候,可以用最小的力氣換上另外一個有實作指定介面的物件,這個好處尤其在如果不小心用了一個不再維護的第三方函式庫、或是需要換另外一個實作更好的第三方函式庫的這一類情境最有幫助。

和介面設計很有關係的,就是 Dependency Injection 。

物件導向的基礎和設計模式

基礎一向是最重要的,像是物件導向的繼承多型封裝封裝的能力算是這三個我覺得最基礎最應該要會的,是讓一大串程式碼,變得看起來比較有邏輯性、可讀性的第一步。

接著就是 SOLID ,如果能夠靈活的運用物件導向三個原則,下一步就是 SOLID ,讓原本封裝起來的物件或是方法,更加有邏輯性的區分開來。

再來就是 Design Pattern 了,從運用情境及碰到的問題,找到適合的方法來優化目前的程式碼(或可以說是重構)。多熟悉各種的 Design Pattern 也有助於自己碰到問題的時候,有更多招式可以拿出來用。

結語

架構設計其實是很困難的一個領域。如何搭配「商業模式」、「產品性質」、「團隊運作方式」等等設計出適當的架構我覺得更加困難。

設計的原則,我自己還是會專注在解決問題、提高品質及不過度拉高日後維護及運行成本,並同時不扯到業務團隊的後腿等等的方式。

關於設計的靈感或點子,可以多跑跑不同跟自己的專業不同領域的研討會或聚會,看自己的世界以外的人,怎麼在他們的地盤解決問題的。問題通常是會在不同領域都會發生的,因此聽到的東西通常會是有可用之處,即使語言、職種或是處理情境不同,轉換一下,說不定就可以解決眼前碰到的問題了。

Show your support

Clapping shows how much you appreciated Li-Hsuan Chen’s story.