Service Oriented 的 iOS 應用架構

Intro

    前不久咱們上線了一款新的 App - Glow Baby,App 針對 0 - 12 個月大的新生寶寶,提供爸爸媽媽全面、健康、科學的育兒知識,幫助記錄寶寶成長的點點滴滴。在 Glow Baby 的開發中,咱們也作了一些新的嘗試 - 使用 Swift 開發,並基於 Swift 的語言特色設計了新的 iOS App 架構。數據庫

    除了 Community 這部分的代碼是做爲一個私有的 Repo 引入,Glow Baby 基本是 100% Swift 代碼。Glow Baby iOS 團隊都是第一次接觸 Swift,過程當中咱們踩過不少坑,遇到過不少抓狂的問題。但整體上,寫 Swift 更加有趣,全部的努力最終也證實是值得的:App 運行更加流暢,代碼更整潔可讀性更高,咱們開發效率也大大提升。編程

    Baby App 跟 Glow 的其餘幾個 App 都是較爲複雜的 App,由於像日記記錄、本地存儲、網絡請求,服務器端數據的同步這些技術難點都有涉及。這些問題也都要求多線程編程。特別是數據同步,如何增量記錄數據的增刪改,什麼時機跟服務器端進行同步。解決這些技術難題是很是有意思的工做,也是架構設計的創造性和樂趣所在。設計模式

    接下來的一系列文章咱們來看看 Baby App iOS 的應用架構。有些設計是基於 Swift 的語言特色的考慮,但並不妨礙總體的架構思路被應用在 Objective-C,甚至 Android 的 App 上。安全

MV(X)

    在介紹 Glow Baby 的應用架構以前,先來看看目前 iOS 上最基礎的架構 MVC,以及爲解決 MVC 的毛病而誕生的其餘幾個架構,如 MVVM、VIPER 等。服務器

    Cocoa 的不少技術跟架構都是基於 MVC。並且不管是文檔、示例代碼,仍是建立一個項目時提供的模板代碼,Apple 都鼓勵開發者去使用 MVC。MVC 定義了 App 裏對象的角色(Model-View-Controller),以及他們之間的交互方式:網絡

  • Model: 表示業務數據對象
  • View:展示數據的 UI
  • Controller:Model 跟 View 之間的粘合劑。一方面對 View 上的行爲做出反應,一般會涉及到 Model 的更改;另外一方面將 Model 的改動反映到 View 上

    因爲 Controller 做爲粘合劑的存在,View 和 Model 只須要跟 Controller 交互,而不知道另外一方的存在。這樣,View 和 Model 做爲獨立可複用的組件,Controller 裏處理業務邏輯。聽起來這樣的架構很清晰直觀,實際應用中,MVC 對於不是很複雜的 App 也是很是高效的。但對稍複雜些的 App,MVC 使用起來就會很是吃力。多線程

    你可能聽過 MVC 也被簡稱爲 Massive View Controller,這就是緣由所在 - View Controller 承擔的職責太多:架構

  • 網絡請求
  • 數據訪問和存儲
  • UI 的調整和組合
  • 業務邏輯
  • View 的 delegate、data source
  • 狀態的維護

與單一責任準則(Single Responsibility Principle)背道而馳。過於臃腫的 View Controller 使 App 的維護成本很是高。咱們的第一個 App - Glow 其實就是這個樣子,儘管咱們已經把網絡請求以及數據訪問和存儲放到了 Model 裏,但因爲對象邊界的定義不夠清晰,大部分 View Controller 依然很臃腫,上千行的 View Controller 很常見。關於 View Controller 有個準則:若是一個 View Controller 超過了 300 行代碼,那它必定作了責任範圍之外的事。更不幸的是因爲一些職責移交給 Model,致使 Model 也變得臃腫起來。原來惟一能夠作 Unit Test 的 Model 如今測試也很困難。less

爲解決 Massive View Controller 的問題,MVVM、VIPER 等架構應運而生。這裏再也不詳細介紹這些架構,有興趣的讀者能夠自行去 Google。工具

Baby App 沒有使用 MVVM 和 VIPER。由於:

  • 不夠直觀,提升了總體代碼的複雜度,對於新入職的員工有必定學習成本
  • 要發揮 MVVM 的優點,須要有 Reactive。Reactive 增長學習成本的同時,也讓調試變得更困難。
  • VIPER 雖然能平衡責任的分配,但因爲引入過多對象,維護成本高。一個簡單的頁面也要求新增多個類和大量傻瓜代碼

因此咱們結合本身的需求和 Swift 的語言特色設計了面向服務的架構(Service Oriented Architecture)。

Service Oriented Architecture

    面向服務的架構在服務器端的開發中很常見,它把業務分紅了多個邏輯獨立的組件。一個組件至關於一個 Service,封裝了與其業務相關的功能,如 UserService 負責用戶的註冊、登入等,而 BabyService 有 Baby 的增長、移除、以及數據的記錄等。Glow 服務器端的架構實際就是面向服務的。在 Baby App iOS 架構中引入 Service 的概念,是 App 開發過程當中迭代的結果,靈感也是來自咱們服務器端的架構。

    能夠看到,Service 是對整個架構縱向邏輯切分的結果。拋開業務邏輯談 Service 意義不大,Service 一般與數據庫表的設計緊密相關。

橫向的邏輯切分將 Baby App iOS 的架構自上而下切分紅三個層(Layer):

  • 應用層(Application Layer)
  • 服務層(Service Layer)
  • 數據層(Data Access Layer)

    服務層和數據層把複雜的邏輯封裝起來,做爲 Framework 提供接口給上層調用。應用層只能調用服務層暴露出來的接口,而不能直接調用數據層。層次結構增強了可重用性和可測試性。應用層調用服務層提供的簡單接口得到數據或者實行用戶操做。服務層也不須要知道數據層中網絡請求,服務器同步,以及數據持久化的具體實現。服務層,數據層,以及應用層都能很容易實現各自的單元測試(Unit Test)。

    Framework 是很棒的工具。把服務層和數據層打包成 Framework,不只幫助構建解耦可重用的代碼,同時 App 的結構和業務邏輯也更加清晰。

應用層(Application Layer)

    應用層也能夠叫展現層(Presentation Layer),負責 UI 跟 展現邏輯。從 Code 角度說,就是 UIView 跟 UIViewController 的集合。複雜的邏輯都封裝到了下層,UIViewController 就變得十分輕量。在 Glow Baby 中,一個 View Controller 一般 200 至 300 行代碼之間,主要負責三件事:

  1. 從 Service 得到數據(ViewModel)並展現
  2. 響應用戶操做,調用相應的 Service 接口
  3. 監聽 Service 層發出的消息,並執行相應操做,如更新 UI

    從 Service 獲取的 ViewModel 實例並非 NSManagedObject 或者其餘持久化的 model 實例,跟 MVVM 中的 ViewModel 也不同。在 Baby App 中,它只是簡單的 Swift Struct,提供應用層須要的數據值。使用 Struct 的好處主要是:

  • 值類型(Value Type): 簡單、容易理解,線程安全
  • 鬆耦合的 View Controller,減小 View Controller 之間可能的交互
  • 減小了 Statefulness 和 Mutability
  • 更高效、佔用更少內存

    使用 Struct 也就意味着想要底層持久化 Model 的更改放映到 UI 上,你必須經過 Service 再抓一次數據。也許有人認爲這是使用 Struct 的一個缺點。其實不是,這應該是優勢。由於 Immutable 的 ViewModel ,讓 View Controller 變得更加簡單,你不用擔憂其餘地方的代碼會更改你的 ViewModel 實例。調試起來也會更加方便,代碼更容易理解、可讀更高。WWDC 中有好幾個視頻都對 Struct 的使用和優點進行了詳解。

    Baby App 支持 Theme,所以 Baby App 的 View Controller 還會調用 Theme 對象來設定 UI 的樣式。但對 View 樣式的設定都封裝在了 Theme 裏,因此並無增長太多代碼量及 View Controller 的複雜度。

服務層(Service Layer)

    服務層定義了一系列 Service 和供給應用層使用的 ViewModel。Service 封裝了 App 主要的業務邏輯,負責把底層持久化的 Model 和網絡請求返回的 JSON 轉換爲 ViewModel,再提供給應用層使用。這樣的分離即增強了 Immutablility 和 Statelessness,也讓應用層中的 ViewController 更輕量,只需幾行 Service calls。Service 雖然承擔大部分業務邏輯,但一個 Service 一般也就 300 行左右的代碼量,這得益於數據層的封裝和抽象。

數據層(Data Access Layer)

數據層的做用是提供簡化的數據訪問接口,主要有 3 個模塊:

  • 數據存儲(Persistence)
  • 網絡請求(Network)
  • 數據同步(Data Synchronization)

    數據存儲咱們使用的是 Core Data,也能夠用 Realm 或者其餘數據庫代替。網絡請求咱們使用了 Moya 進行抽象,使 API 的設計和調用更簡潔,並支持咱們 Server 自定義的錯誤。數據同步模塊,會自動同步本地和服務器端的用戶數據。

Conclusion

    在 iOS 上,MVC 因 Controller 的臃腫而遭到衆人詬病。但其實 MVC 做爲最基礎的設計模式,展示了一個架構的精髓 - 抽象分離。這是咱們應該學習思考的,而不是盲目從其餘架構模式中選擇一個來代替 MVC。Glow Baby iOS 的架構從能夠看做是一種 MVC。從總體看,數據層是 Model,服務層是 Controller,應用層是 View。而若是看細節的地方,應用層跟服務層提供的 ViewModel 也能夠看作一個 MVC:ViewModel - UIViewController - UIView.

    設計架構也沒有「最好」或者「最正確」的方式,設計自己就是一項極具創造力的工做。但架構是有好壞區別,一個好的架構應該是對團隊成員最爲直觀,同時擴展性良好的架構。

相關文章
相關標籤/搜索