文档章节

Service Oriented 的 iOS 应用架构

zh_iOS
 zh_iOS
发布于 2016/06/16 20:29
字数 2511
阅读 694
收藏 3

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 现在测试也很困难。

为解决 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.

    设计架构也没有「最好」或者「最正确」的方式,设计本身就是一项极具创造力的工作。但架构是有好坏区别,一个好的架构应该是对团队成员最为直观,同时扩展性良好的架构。

 

 

本文转载自:http://tech.glowing.com/cn/service-oriented-ios-architecture/

zh_iOS
粉丝 27
博文 79
码字总数 35915
作品 0
石景山
程序员
私信 提问
蚂蚁金服客户端招聘---求简历

职位描述 理财类金融产品应用(Android/IOS版本)的架构设计和开发工作; 根据产品需求开发相关feature,配合市场等其他部门,提供产品相关技术支持; 研究新技术、新架构,并能应用和推广到产...

LooperJing
04/26
0
0
求Web 前端/全栈开发/Ios移动/Big Data Engineer

1.Geek型创新公司。2.顶级办公环境。3.近乎完美的福利。4.竞争力的薪水+期权。 如果感兴趣可发邮件到: jxiao@hiringby.com. 喜欢开源,敏捷优先,经常出没Github社区优先,喜欢大数据及移动...

Jack_hiringby
2015/01/08
559
5
iOS 终于支持了 PWA,一起来认识一下这个强化版「小程序」 | 科普

iOS 终于支持了 PWA,一起来认识一下这个强化版「小程序」 | 科普 发布于 2018-04-13 文章被以下专栏收录

少数派
2018/07/06
0
0
使用UniDAC在Delphi XE4中开发iOS应用

使用UniDAC在Delphi XE4中开发iOS应用   核心提示:Delphi XE4的发布让很多Delphi开发者欣喜若狂,毫无疑问它使得Delphi开发更简单且更容易被理解,而且让开发无论是iPhone、iPad还是iPod的...

vga
2014/06/01
572
0
软件架构杂谈(三) --- APNS

浅谈软件架构(三) —— APNS ----cnyinlinux 本文将讨论的是常见软件架构中的三角关系——APNS。 APNS是Apple Push Notification Service的简称,即苹果推送通知服务。它用在当前很火热的苹果...

cnyinlinux
2015/06/09
328
0

没有更多内容

加载失败,请刷新页面

加载更多

实战项目-学成在线(一)

之前看的黑马程序员实战项目之一,打算以博客的形式写出来,也让自己重新温习一下。 1、项目背景 略(就是当前这东西很火,我们重点在开发,这些就略过) 2、功能模块 门户,学习中心,教学管...

lianbang_W
27分钟前
2
0
基于Vue的数字输入框组件开发

本文转载于:专业的前端网站➫基于Vue的数字输入框组件开发 1、概述 Vue组件开发的API:props、events和slots 2、组件代码 github地址:https://github.com/MengFangui/VueInputNumber 效果:...

前端老手
35分钟前
2
0
百度地图根据经纬度获取运动轨迹

<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=n......

泉天下
37分钟前
4
0
学习记录(day04-axios增删改查、v-for循环、页面加载成功处理函数)

[TOC] 1.1 基本语法:插值表达式 <template> <div> {{username}} <br/> {{1+2+3}} <br/> {{'你的名字是:' + username}} <br/> {{'abc'.split('')}} </div><......

庭前云落
今天
3
0
CentOS Linux 7上将ISO映像文件写成可启动U盘

如今,电脑基本上都支持U盘启动,所以,可以将ISO文件写到U盘上,用来启动并安装操作系统。 我想将一个CentOS Linux 7的ISO映像文件写到U盘上,在CentOS Linux 7操作系统上,执行如下命令: ...

大别阿郎
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部