移动端工程架构与后端工程架构的思想摩擦之旅(1)

此文已由作者黎星授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验


记资源投放后端工程的架构调整与优化 


架构思考


一直以来对软件工程架构有着极大的兴趣,无论是之前负责的移动端Android工程,亦或是现在转到后端开发后维护的资源投放工程。可以说一个团队中并非每个开发都能够深入掌握架构知识,但需要每个人能够拥有软件架构的意识。架构是对工程整体结构与组件的抽象描述,是软件工程的基础骨架。架构在工程层面不分领域,且思想是通用的。引用维基百科对于软件架构的定义^1:


软件体系结构是构建计算机软件实践的基础。与建筑师设定建筑项目的设计原则和目标,作为绘图员画图的基础一样,软件架构师或者系统架构师陈述软件架构以作为满足不同客户需求的实际系统设计方案的基础。从和目的、主题、材料和结构的联系上来说,软件架构可以和建筑物的架构相比拟。一个软件架构师需要有广泛的软件理论知识和相应的经验来实施和管理软件产品的高级设计。软件架构师定义和设计软件的模块化,模块之间的交互,用户界面风格,对外接口方法,创新的设计特性,以及高层事物的对象操作、逻辑和流程。


架构的合理设计可以解决面对复杂系统时可能面临的很多问题,例如:

  • 业务边界与模块职责划分问题

  • 代码权限控制问题(数据库不应直接被业务方调用)

  • 代码重复,逻辑分支多,坏味道多的问题

  • 由于考虑不周,可能存在隐藏bug

  • 修改一个逻辑需要修改N个地方代码逻辑

从实际的实践来看,的确如此。以前在移动端做的架构设计流程,在后端重新得到了实践。


移动端架构思考


尚未接触到强大的Spring容器之前,我一直探索着在移动端有一种能够在编译期暴露服务声明,运行时自动注入实现类的做法;接触到Spring以后,得以理解这其实就是IoC容器的概念。Android的组件化思想,以及网上发布的各类组件化的技术文章,给了我很多值得借鉴的思路。客户端的代码一般是以module来组织的。一个module,既可以配置成为一个独立发布的库,也可以编译成一个单独的apk。组件化的概念正是利用了module这一特点,将一个大工程中的业务拆分成一个个module,各个module间的业务相对独立,组件间通过各自暴露的业务接口实现通信。基于此思想的移动端架构模型可以使用下图来表示。

20181206135052224b07c1-f315-47dd-9e6a-0edeaf8d7e12.png


点击查看原图

该架构模型由5个部分组成,分别是Toolkit/ToolkitSDK module、基础组件库/基础组件库module、基础服务接口/业务服务接口module、服务调度中心module以及业务module。

  • Toolkit/Toolkit SDK

Toolkit是工具类及与工具类相关的SDK的集合。工具类属于工程架构里最基础的模块,提供了通用的方法与工具类服务(工具类服务是指可以被抽象成一个独立的与业务无关的基础服务,如缓存、数据库操作等)。工具类通常作为最底层的module,被其他所有模块引用。

  • 基础组件库/基础组件SDK

基础组件库是基础组件及相关SDK的集合。基础组件库提供与业务相关的基础组件,是构建一个移动端应用所需要的通用组件的集合。它与工具类的区别在于基础组件库可能会包含少量业务逻辑代码,是无法拆分给其他应用使用的;另一方面,基础组件库是基础服务接口的实现,是不对业务层暴露的,避免了业务层与基础SDK打交道,有利于整体替换底层基础框架的实现(例如Volley替换为OkHttp、Fresco替换为Glide)。

  • 基础服务接口/业务服务接口

基础服务接口声明了一组通用的基础服务,业务层通过基础服务接口获取基础服务,如网络请求、图片加载等。业务服务接口声明了一组该模块提供给其他模块的服务,业务之间的通信也是通过服务接口来完成的。例如首页模块需要获取购物车的商品数量,首先通过服务调度中心获取购物车的服务接口,再通过服务接口调用购物车获取商品数量的接口方法即可。

  • 服务调度中心

服务调度中心,是一个接口收集与管理的容器。服务调度中心将所有基础服务接口与业务接口收集起来,通过一定的方式与它们的实现类进行绑定。所有的业务都需要通过服务调度中心才能够获取到服务。服务的注册与发现和Spring容器的IoC思想是类似的。

  • 业务层

业务层是每个业务的具体实现的集合。业务层的业务之间是没有直接引用关系的,业务层提供了业务服务接口中暴露的服务的具体实现。业务之间的通信需要通过服务调度中心获取其他业务的服务接口。


移动端架构小结

通过接口服务架构模型,模块之间是高度解耦的。业务负责人唯一需要维护的公共部分便是这个模块在业务服务接口中暴露的服务。对于业务服务的接口功能增改变得非常方便,业务实现的逻辑更改、代码优化等,只要不改变服务接口的签名,就不需要其他业务方改动任何代码即可完成,由此团队的开发效率是非常高的。


后端架构思考


对于后端工程来说,架构的设计与实现必定是与工程的业务难度及复杂程度相关的,如果只是很简单的业务模型,就没有必要弄得太过复杂,避免得不偿失。本人只接触了几个月后端知识,对于后端的架构体系与演进过程处于不断地学习和探索中。投放系统是我接触到的第一个完整的后端工程,其中Web工程采用传统的MVC架构^2,对我具有很大地学习和借鉴意义,项目架构如下图所示。

20181206135101f1c38e12-2589-40e3-a527-49aec9ba96dc.png


点击查看原图

该架构纵向划分成展示层、控制层、服务层、对象关系映射层和数据服务层5个部分,层级间通过AOP的方式插入了业务监控、日志、权限控制、统计分析等功能。

  • 展示层(View)

展示层是系统与用户打交道的地方,提供与用户交互的界面。对于用户而言,只有展示层是可见的、可操作的。展示层对于某些工程来说不是必须的,例如提供纯后台服务的工程。

  • 控制层(Controller)

主要负责与Model和View打交道,但同时又保持其相对独立。Controller决定使用哪些Model,对Model执行什么操作,为视图准备哪些数据,是MVC中沟通的桥梁。在Controller层提供了http服务供展示层调用。在依赖管理中,控制层需要依赖服务层提供服务。

  • 服务层(Service/Facade)

服务层是业务逻辑实现的地方,上层需要使用的功能都在服务层来实现具体的业务逻辑。服务层就是将底层的数据通过一定的条件和方式进行数据组装并提供给上层调用。服务层可以拆分为业务接口和业务实现,业务实现可以对外部隐藏。在投放工程中,控制层既依赖了业务接口,又依赖了业务实现。后面的改造我们可以看到,编译期红色线依赖是完全没有必要的。服务层需要依赖数据关系映射层与持久层的数据打交道。

  • 对象关系映射层(ORM)

对象关系映射层的作用是在持久层和业务实体对象之间作一层数据实体的映射,这样在具体操作业务对象时,只需简单的操作对象的属性和方法,不需要去和复杂的SQL语句打交道。ORM使得业务不需要关心底层数据库的任何细节,包括使用的数据库类型、数据库连接与释放细节等。对象关系映射层只依赖数据服务层提供服务。

  • 数据服务层(Data Server)

数据服务就是提供数据源的地方。数据服务可以提供持久化数据及缓存数据。持久,即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。而缓存是将信息(数据或页面)放在内存中以避免频繁的数据库存储或执行整个页面的生命周期,直到缓存的信息过期或依赖变更才再次从数据库中读取数据或重新执行页面的生命周期。数据服务层是数据源头,处于架构的最底层。


后端架构小结

后端工程,更加注重层级的概念,每一层的职责非常明确。展示层负责与用户进行页面交互,控制层合并业务数据并控制View的展示,服务层则是实现业务逻辑的聚集地,对象关系映射层在业务层和数据服务层之间建立通道,而数据服务层则提供数据。总体而言,投放工程的MVC架构给我的感觉是比移动端架构复杂,层级多,职责分工明确,带来的问题是层级间的交互也比较麻烦。另外服务层里承载了几乎所有的业务逻辑,层级偏重,如果没有好好地梳理业务逻辑划清边界,很容易把服务层搞成一锅粥。清晰的模块职责划分,可以帮助服务层更好地为控制层服务。


架构思想的摩擦


可以看到,客户端与后端有着非常相似的架构模型。

  • 从代码组织的角度:以module作为层级代码组织的基本工具,分为工具库、基础组件库(中间件)、服务接口/API、服务层/业务层、视图层等。module间的依赖关系几乎是一样的。

  • 从业务模型的角度:后端工程分为交易组、商品组、售后组、客服组等,对应移动端的交易链路浮层、商品详情页、售后详情页、帮助与客服页等,每个业务是由不同的组负责的,业务之间通过约定的接口相互提供服务,各种各样的业务模型聚合成了整个系统。

  • 从功能服务的角度:分为业务服务接口的暴露、业务服务实现的隔离、业务服务的查找与注册。

下面从功能服务的角度,详细说明本文在思想摩擦过程中想要表达的观点。


业务接口的作用


业务接口,可以认为是这组业务向外暴露其功能的一套标准。标准一旦形成并发布,就需要业务方持续维护这套标准,使得标准变得完善和稳定。同时标准可以更新升级,可以通过版本来实现,提供新的功能。业务接口一般具备以下特性:

  • 业务接口包含一组Java的接口集合以及与这些接口相关的POJO,通常打包成一个JAR/AAR包。

  • 业务接口只提供接口功能的定义,不包含任务业务逻辑。

  • 业务接口可以进行版本管理,一旦版本发布,则该版本的接口不再可变。业务需要新增功能时,只需要在原有业务接口的基础上,增加新的功能接口或方法,同时升级业务接口版本号并发布。

其他业务方需要使用该业务的功能,只需要引入该业务的JAR/AAR包,通过服务调度中心获取该服务接口即可。


业务逻辑的存放


业务接口仅提供了功能的定义,不包含任何业务逻辑。那么,业务逻辑(即接口的实现类)放哪里呢?不管是移动端架构还是后端架构,在工程领域,业务逻辑在任何时候都不应该对业务的使用方暴露。这样做有两个好处:

  • 业务方只关心功能,不关心功能实现的过程。隐藏业务的实现逻辑可以降低业务方使用该功能的成本及复杂度。

  • 业务功能的后端逻辑改动及必要的技术优化、性能优化,只要不更改接口签名,则不会影响当前的业务方使用。

一般情况下,业务的逻辑实现会放在单独的业务模块中,该业务模块仅限工程内部引用。后端传统的MVC工程架构把业务的实现逻辑放在了Service/Facade层,层级之间的类不相互引用;而基于驱动领域的设计模型把业务的实现逻辑限定在了一个领域/子域里,领域之间通过界限上下文绑定。在移动端,时下较为热门的众多组件化方案,也是将一个独立的功能模块作为单独的module,module可以独立编译为apk,也可以通过aar的方式集成到主Application中。


业务逻辑的隔离


业务的使用方包括工程内部的上层业务和外部服务,业务接口与业务逻辑有必要进行代码级别的隔离,这样才能避免上层业务引用到业务逻辑的代码。通常,工程的每个模块负责不同的功能,模块之间的引用关系通过依赖管理工具(如Maven或Gradle)来配置。我们可以巧用依赖管理工具的runtime compile机制来实现运行时依赖。即在编写对外接口的时候,不直接引用包含业务逻辑的module,等到编译的时候再把业务逻辑代码一起编译进来,然后在运行时通过一定的方式调用对应的业务逻辑。

  • Maven通过在dependencyManagement的依赖中加入runtime标签实现^3。

  • Gradle通过在dependencies将compile改为runtime实现^4。

下面的两张图简单介绍了后端工程和移动端工程基于业务逻辑隔离的工程架构思路。其中,虚线表示runtime compile依赖,实线表示正常的依赖关系。

20181206135112976bcb17-f32b-4964-bf9c-bb01b9101300.png


点击查看原图

工程的启动入口几乎不包含业务代码,只包含配置文件。Controller层是一组RestfulApi的集合,给前端和客户端提供http请求服务,业务接口/API是一组dubbo接口,给其他工程业务方提供RPC调用。Controller层在编码的时候只依赖Service/Facade接口,在编译期依赖Service和Facade接口的实现。这样设计还有一个好处是对DAO层的保护,DAO层只和Service层打交道,Controller以及对外提供的dubbo接口是引用不到的,更好地保护数据安全。

20181206135119781bc60a-e56d-46ab-b555-2863ef9910e7.png


点击查看原图

在移动端的架构中,单Application+多module已经成为主流。每个module负责一块独立的业务,如首页、订单、购物车等,核心模块也可以拆分为独立模块,如网络引擎、图片引擎。这些独立的模块可以抽离出BusinessService/CoreService服务接口,模块间的交互只需要通过Service接口通信即可,业务对于其他的p_CoreSDK/Business的逻辑实现是不可见的。


业务接口的注册


有了业务接口和业务实现,还需要一种在运行时把它们“粘合”起来的工具,这一过程可以称为业务接口的注册。当业务方访问业务接口时,这个工具需要帮助我们查找到对应的业务实现。控制反转(IoC)或依赖注入(DI)的思想给我们提供了解决办法。

在后端工程中,Spring是最为常用的IoC容器之一。Spring在运行时根据配置文件或注解动态生成对象,再由变量注解通过Java反射注入到对应的实例中。因此代码中只需要通过在全局变量声明相应的注解即可完成业务接口的注册。

移动端由于有限的硬件资源,更多地把CPU时间分配给了页面渲染,保证应用体验流畅,不太可能在应用启动的过程中大量通过反射生成实例对象,因此移动端并没有出现Spring框架。尽管如此,依赖注入的思想是通用的。通常移动端只需要保证在运行时能够获取到对应的业务实现,几乎没有在运行时动态改变业务实现的需求,聪明的工程师想到了把服务的注册提前到编译期进行,这一过程可以使用JDK提供的Annocation Processing Tool完成(例如Dagger2),也可以在编译生成Class字节码以后使用ASM操作字节码注册实现(如ARouter)。


 

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击。


相关文章:
【推荐】 如何学习、了解Kubernetes?
【推荐】 分析自己遇到的Excel导出报NullpointException问题
【推荐】 当我们谈论计划时我们在谈论什么

转载于:https://www.cnblogs.com/zyfd/p/10077010.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/395803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

View野指针问题分析报告

【问题描述】 音乐组同事反馈了一个必现Native Crash问题&#xff0c;tombstone如下&#xff1a; pid: 5028, tid: 5028, name: com.miui.player >>> com.miui.player <<< signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 79801f28r0 7ac59c98 r1 …

SicilyFunny Game

一、题目描述 Two players, Singa and Suny, play, starting with two natural numbers. Singa, the first player, subtracts any positive multiple of the lesser of the two numbers from the greater of the two numbers, provided that the resulting number must be non…

java 分布式同步_Java Web分布式集群搭建(三)——Session同步

对于一个业务系统的Tomcat集群来说&#xff0c;必须保证同一个用户访问到任一台服务器上都可以维持之前操作的身份。比如在服务器A进行了登陆&#xff0c;那么在服务器B中也要同步该用户已登录的状态&#xff0c;这里就用到了Session的同步。同步方式sticky模式、复制模式、Ter…

移动应用程序和网页应用程序_如何不完全破坏您的移动应用程序的用户界面

移动应用程序和网页应用程序by Luke Konior卢克科尼尔(Luke Konior) 如何不完全破坏您的移动应用程序的用户界面 (How to not utterly ruin your mobile app’s user interface) There’s no single universal formula for designing a great user interface (if you discover…

logging记录日志

日志是一个系统的重要组成部分&#xff0c;用以记录用户操作、系统运行状态和错误信息。日志记录的好坏直接关系到系统出现问题时定位的速度。logging模块Python2.3版本开始成为Python标准库的一部分。 日志级别 在最简单的使用中&#xff0c;我们直接导入logging模块&#xff…

C#编程之接口

1.定义 接口是把公共方法和属性组合起来&#xff0c;以封装特定功能的一个集合。&#xff08;一旦定义了接口&#xff0c;就可以在类中实现它。这样类就可以支持接口所指定的所有属性和成员&#xff09; 注意1&#xff1a;接口不能单独存在。不能像实例化一个类那样实例化一个接…

supervisor守护进程

2019独角兽企业重金招聘Python工程师标准>>> supervisor 是一个client/server系统,把不是守护进程的进程变成守护进程,并监控和控制类 Unix 操作系统上的进程。 upervisor就是用Python开发的一套通用的进程管理程序&#xff0c;能将一个普通的命令行进程变为后台dae…

神经网络算法 java 源代码_神经网络算法与实现 ——基于Java语言 代码实例

【实例简介】Neural Network Programming with Java_ISBN 978-7-115-46093-6【实例截图】【核心代码】NeuralNetworkProgrammingwithJava_code└── Neural Network Programming with Java_code├── Chapter1│ ├── HiddenLayer.java│ ├── InputLayer.java│ ├…

javascript面试_在编码面试中需要注意的3个JavaScript问题

javascript面试JavaScript is the official language of all modern web browsers. As such, JavaScript questions come up in all sorts of developer interviews.JavaScript是所有现代Web浏览器的官方语言。 因此&#xff0c;各种开发人员访谈中都会出现JavaScript问题。 T…

【学习笔记】深入理解js原型和闭包(11)——执行上下文栈

继续上文的内容。 执行全局代码时&#xff0c;会产生一个执行上下文环境&#xff0c;每次调用函数都又会产生执行上下文环境。当函数调用完成时&#xff0c;这个上下文环境以及其中的数据都会被消除&#xff0c;再重新回到全局上下文环境。处于活动状态的执行上下文环境只有一个…

Java基础--访问权限控制符

今天我们来探讨一下访问权限控制符。 使用场景一&#xff1a;攻城狮A编写了ClassA&#xff0c;但是他不想所有的攻城狮都可以使用该类&#xff0c;应该怎么办&#xff1f; 使用场景二&#xff1a;攻城狮A编写了ClassA&#xff0c;里面有func1方法和func2方法&#xff0c;但是他…

css绘制正方体_设计师仅使用CSS绘制了8个标志性X战警

css绘制正方体Here are three links worth your time:这是三个值得您花费时间的链接&#xff1a; A designer drew 8 iconic X-Men using nothing but CSS (1 minute interactive) 一位设计师仅用CSS绘制了8个标志性的X战警( 互动时间为1分钟 ) Raspberry Pi just turned 5. H…

Dubbo简单介绍及实例

1、概念 Dubbo是一个分布式服务框架&#xff0c;以及阿里巴巴内部的SOA服务化治理方案的核心框架。其功能主要包含&#xff1a;高性能NIO通讯及多协议集成。服务动态寻址与路由。软负载均衡与容错&#xff0c;依赖分析与降级等。 说通俗点&#xff0c;就是首先将程序组件化成一…

Oracle 10.2.0.5升级至11.2.0.4

参照MOS 官方文档Complete Checklist for Manual Upgrade to Oracle Database 11gR2 (11.2) (Doc ID 837570.1)一、升级前的准备1、复制utlu112i.sql脚本从11G数据库复制$ORACLE_HOME/rdbms/admin/utlu112i.sql 脚本至10g 数据库临时目录&#xff0c;准备执行如果不在10g数据库…

脱壳_详细_使用的方法_01

ZC: 如何确定被调试程序已经来到了 未加壳的程序中&#xff1f; ZC:  视频中是使用判断集中语言的特征 ZC:  我的方法&#xff1a;上面的方式 ESP平衡 1、第1课 (1)、单步跟踪&#xff08;原则&#xff1a;向下的跳转>正常F8&#xff0c;向上的跳转>F4跳过(或者用F2…

android 函数式编程_Android开发人员的函数式编程-第1部分

android 函数式编程by Anup Cowkur通过安纳普考库(Anup Cowkur) Android开发人员的函数式编程-第1部分 (Functional Programming for Android Developers — Part 1) Lately, I’ve been spending a lot of time learning Elixir, an awesome functional programming language…

java编程 内存_Java编程技术之浅析JVM内存

JVMJVM->Java Virtual Machine:Java虚拟机,是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。基本认知&#xff1a;1.JVM是用于运行Java代码的假象计算机&#xff0c;主要有一套字节码指令…

bzoj1116: [POI2008]CLO

传送门&#xff1a;http://www.lydsy.com/JudgeOnline/problem.php?id1116 题目大意&#xff1a;Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 你要把其中一些road变成单向边使得&#xff1a;每个town都有且只有一个入度 题解&am…

java排序算法大全_各种排序算法的分析及java实现

排序一直以来都是让我很头疼的事&#xff0c;以前上《数据结构》打酱油去了&#xff0c;整个学期下来才勉强能写出个冒泡排序。由于要找工作了&#xff0c;也知道排序算法的重要性(据说是面试必问的知识点)&#xff0c;所以又花了点时间重新研究了一下。排序大的分类可以分为两…

Cocos2d-x 3.0 简捷的物理引擎

Cocos2d-x 3.0 开发&#xff08;九&#xff09;使用Physicals取代Box2D和chipmunk http://www.cocos2d-x.org/docs/manual/framework/native/physics/physics-integration/zh -- 官网Demo 水墨鱼的专栏 http://www.cocos2d-x.org/docs/catalog/zh --- 官方 搭“server” 须要哪…