摘要:介绍下一个新项目,后端该如何从零去搭建。我们先假设这个项目由两部组成提供给站点使用的提供给运营人员使用的管理后台。因此通过回顾,我们得出我们的后端项目需要一个的层次,来存放业务逻辑。
这是 后端开发者从零做一个移动应用 的后端部分第二篇。介绍下一个新项目,后端该如何从零去搭建。我们先假设这个项目由两部组成
提供给wap站点、app使用的api;
提供给运营人员使用的管理后台。
整个项目采用 Phalcon,项目的demo可以 点这里 参阅
备注:跟随文章进度,项目持续更新,最后会与配套的wap app形成一个整体
项目最终至少会包含以下内容:
小米消息推送
支付集成(支付宝、招商、微信)
基于 Codeception 的api测试
登陆api(这部分采用oauth2,会基于 "bshaffer/oauth2-server-php" 做)
项目结构回顾后端系统一般都是采用 MVC 结构(这里均以PHP为例),M 代表模型,V 代表视图,C 代表控制器。我在啰嗦几句
Model指的是数据模型,这个数据模型包括你的Mysql中的表结构,或者redis的缓存对象结构都可以。它代表一个数据操作单元。
View指的展示给用户浏览、直接操作的界面,这个大家都懂,不多说
Controller 控制器,主要是为了隔离 View 与 Model 直接打交道,他做为一个中间人,两头传递小纸条。
在我过往的项目中,我主要的困惑在于,业务逻辑是放在 C 还是放在 M。
从对象角度出发,业务逻辑无非就是操作数据,要么读取,要么修改,那么应该放在M层,因为一个对象应该有自己的属性与方法。
业务放在M中实际工作中我们常常有这样的场景,比如:读取一个游戏列表数据,数据包括游戏的详情以及游戏的版本信息以及下载信息。因为游戏app会存在升级,因此一个游戏会对应多个包。那么这里至少存在两个model
游戏详情model,包括游戏的名称,logo等基本信息
游戏的包信息model,包括包所属平台,大小,下载地址,版本信息等
那么这个动作的方法应该封装在哪里呢?以前的做法是,分别封装对应的操作到对应的model,然后在控制器中分别调用。说回到这里,游戏model封装了查询游戏列表的method,然后包model封装了根据游戏id查询包信息的method。
然后我们在控制器中分别调用这个两个方法,然后再进行组装,把游戏对应的包设置到对应的游戏中。
那么有一个问题,假设我们在游戏详情这个控制器方法中,需要返回一个相关游戏的集合,难道又重复一次上面的操作?
有人会说把处理游戏部分抽离成一个公共方法,那么假设是要在新闻详情里边调用呢?这根本不该在同一个控制器里边啊!
上面我们把方法放在model中遇到了复用的小麻烦,那么继续看看放到controller中会怎样?
这个时候的一个好处是:我们可以使用连接查询,将刚刚的2次查询,通过连接查询1次完成,对于mysql的时间减少了,程序性能提升,然后对查询结果啪啪啪处理完成。
好吧,不往后面说了,相信大家已经发现了,这个查询过程还是不可复用。自然而然的,我们这里应该想到,将它提炼成一个方法,无法满足其他控制器使用(一个控制器调用另外一个控制器的想法想都别想啊)。那么只能提炼成一个类了,这个类来封装所有的业务。
这样之后,任何需要游戏列表数据的地方,直接调用这个GameServer(假设封装的业务逻辑都放在xxxServer中)就可以获得相同的数据,然后如果业务变动,我们也只需要改动这一处,所有地方得到的数据也将会是一致的。
因此通过回顾,我们得出我们的后端项目需要一个server的层次,来存放业务逻辑。
Server层存在的意义分离出来的这一层,集中涵盖了所有的业务功能,极大的提高了代码的复用性,除了不同控制器不同方法的直接使用,还包括了不同模块之间的复用。
但是在不同模块之前服用,server层也需要考虑一些额外的东西,比如我们有一个app api模块,有一个后台管理模块。那么都是获取列表数据,可能给app api模块可能不需要某些字段,但是后台管理需要知悉全部内容,以及后台用户权限上的一些问题。这些部分可以继续进行拆分,与server组合。需要结合自己的业务来进行管理。
我个人实践过程中代码的另外一个好处是,server层从某种层度上让C层变得简单,这让团队中的新人能够快速上手接触代码。比如小明是团队新人,那么在他熟悉所使用框架的前提下,他可以立刻在C层开始做事情,因为这里没有业务,有的只是验证客户端传过来的数据,以及对server层的调用返回。通过这个过程可以加速其融入团队的进程。
统一的返回格式约定api返回的数据格式,这基本上是系统开发开始的第一步,原先常用的方式就是在每个控制器中通过
return json_encode([ "msg" => "ok",// 携带的信息,可以用来前端 alert 提示用户 "data" => [// 具体数据 ... ... ], "code" => "0", // 0表示成功,其他表示对应错误 ])
那么这里首先遇到的第一个问题,为了简化前端对类型的判断,基本上所有的字段值,都是返回字符串形式。那么 data 里边的内容就需要在每个控制器中进行处理字符串、utf-8编码等问题。要重复代码,就算你抽离成一个方法,也需要面对该问题。好点的解决方案是在返回数据的拦截器(每一个框架都有类似的概念)内进行统一的处理。
像上面这样的代码写法,带来的额外问题可能有,字段名称打错,比如: code 写成 cdoe ,data 写成 date。为程序代码额外的风险(尤其是bug修复时最容易出现该情况)
那么一种解决办法就该由此想到,采用对象的方式来规范化返回的数据结构。比如我们定义一个类:
class ResultData { /** * 返回的信息提示 * @var string $msg */ private $msg; /** * 返回的数据结构 * @var array|object|string */ private $data; /** * api 状态码 * @var int $apiCode * @see ApiCode */ private $apiCode; public function __construct(int $apiCode, string $msg = "ok", $data = null) { $this->apiCode = strval($apiCode); $this->msg = trim(strval($msg)); $this->data = $data; } /** * 获取数据结果 * @return array */ public function getRetData() { if (! is_array($this->data) && is_object($this->data) && method_exists($this->data, "toArray")) { $this->data = $this->data->toArray(); } // valueToString 将data的value转化为 string 并且做utf-8转码 $result = [ "code" => $this->apiCode, "msg" => $this->msg, "data" => $this->data ? ArrayUtil::valueToString($this->data) : [], ]; if (! APP_ENV_PROD) {// 测试环境显示 api 的处理时间信息 方便优化 $result["use_time"] = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]; } return $result; } }
有了上面这个类,我们所有的服务层或者controller都应该用它作为返回值。然后在拦截器中统一进行json encode即可。这样子即减少了犯错的可能性,同时对统一处理数据的地方做了统一管理集中到 ResultData 中,那么以后有什么特殊变动,调整一处,处处生效。
其它问题另外还有关于 oauth2 如何集成到项目中等等问题,这部分均放到 x-api 项目中进行说明,纸上说来终觉浅嘛。
日志的记录也是系统开发非常重要的部分,这部分没什么太多说的,用规范的格式,存储指定的数据(介质可以是:db、file)。
系统开发中应该拒绝使用 var_dump、echo 这些方式进行调试,另外建议采用:PhpStorm IDE来进行系统开发。
后续分享接下来会完善一个 x-api 的基本结构,以及php自动化测试部分文档教程,然后后端部分就告一段落。(本系列的分享主要集中在代码层面,不涉及相关系统部署问题)
GitHub:https://github.com/helei112g
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/25613.html
摘要:后端开发的疑惑后端开发最常面对的一个问题性能高并发等等。而到了时代,在方面有了前后端分离概念移动后端更是无力渲染天然前后端分离。 先来上一张前端页面的效果图(Vue + Vux + Vuex + Vue-Router)。showImg(https://segmentfault.com/img/remote/1460000010207850); 第一次做gif 没什么经验,太大了。加载...
摘要:以实现自己熟悉的东西为导向比如我们做后端开发,首先是常用的循环迭代条件判断增删改成。它是由实现的,不保证元素的顺序,也就是说所说元素插入的顺序与输出的顺序不一致。 下面是我直播的文字版,直播地址:https://segmentfault.com/l/15...代码:https://github.com/zhoumengka...整个项目我们我又细分了6个版本来演进,希望更加便于大家对比...
摘要:以实现自己熟悉的东西为导向比如我们做后端开发,首先是常用的循环迭代条件判断增删改成。它是由实现的,不保证元素的顺序,也就是说所说元素插入的顺序与输出的顺序不一致。 下面是我直播的文字版,直播地址:https://segmentfault.com/l/15...代码:https://github.com/zhoumengka...整个项目我们我又细分了6个版本来演进,希望更加便于大家对比...
摘要:先来说说关键字。什么时候用来修饰方法关键字大家都知道是用来修饰方法与属性的。一句话学会面向对象的方式来思考。充分发挥其性能优势,又能解决扩展性差的问题。这里不会进行与的比较。 你以为你知道了一切,只是你以为而已。知识的美妙就在于,一生的时光在它面前显得多么的短暂。 嗯嗯,扯远了,我今天只想说说:static 与 yield。 先来说说 static 关键字。本篇只讲静态方法的使用与后期...
阅读 2440·2021-11-19 09:40
阅读 3562·2021-11-17 17:08
阅读 3750·2021-09-10 10:50
阅读 2180·2019-08-27 10:56
阅读 1888·2019-08-27 10:55
阅读 2622·2019-08-26 12:14
阅读 973·2019-08-26 11:58
阅读 1478·2019-08-26 10:43