前后分离结构的BS项目的一般开发流程

自传

从2008年,上初中的时候,我就开始不务正业,在电脑上各种玩。我不是一个引人注意的人,学习成绩差,上课睡觉,放学上网,可能是从解除flash开始,慢慢的,同学开始注意到我了,用现在的话来说学习计算机技术纯属为了装逼。。。。开始的时候使用flash做一些简单的动画,而后接触到了其中的AS脚本,开始对使用代码表现自己对业务的逻辑这件事感兴趣。可惜英语并不好,换句话说那是的我对英语文档的阅读无法沉得住气,所以我放弃了flash,转而开始学习易语言,一种使用中文就可以表达计算机逻辑的东西,很有意思,最重要的不是它能够中文编程,而是他的帮助,API文档,完全是汉化的,这让那时的我非常神往。估计也是那时网络犯罪未纳入刑法,网络上充斥着大量的黑客教程,初二的我也开始解除一些黑科技,比如拿站,免杀,远控等,那会儿的我还天真的拿易语言去做了个远控,虽然完全是自娱自乐,但可以算的上是我的首个桌面程序,那会儿的杀毒软件还是比较初级的,大多基于特征码,正对于这样的现状,网上流传着如Myccl这样的特征码定位工具,我也是那会儿开始接触到汇编,使用OD,感觉过杀软是一种刺激的开发过程,自己学了Win32汇编,幻想着使用汇编来写桌面程序。。。当我发现做个简单的窗口也要调用Windows最底层的API的时候,果断放弃了。。。初中阶段就这么浑浑噩噩的,写了些东西,这些东西连同我家那台老爷机报废了。

直到高中,不知道什么原因买了本C++的书回来看,算是系统的了解到了些概念,面向过程的,面向对象的,同时也深刻的让我体会到了作为编程人员的两个重要技术指标,也是知道我日后学习的两个重要方向,一个是算法,而另一个是编程资源。算法是对已产生现实问题解决方案的高度抽象,这一部分基本上不是看一本《算法导论》就能搞定的,算法能力的提高就像一种没有结果的成长过程,因为现实所遇见的问题都是新的,或许这些问题的分解你似曾相识,但对于你解决这个问题往往都是杯水车薪。编程资源的范围可以说是非常广的,编程语言、已有系统、平台API、开源项目、开源库等都是编程资源,人或许可以没有牛X的算法功力,但是一定得有相对充实的编程资源积累,因为这是招聘信息里最直观能表现出来的要求,“熟悉掌握XXXX、XXXX、等开源框架”,"熟悉XXX架构",有些公司的人事可能为了省事会直接写“N年以上从业经验”,这一切实际上都是对编程资源的要求。算法和编程资源这两个指标就像现在学科中的理科和文科一样,都是指导产业、个人发展的核心,也正是因为我对自己认为的这两个提现开发者价值的指标深信不疑,慢慢的开始有了辍学的想法,因为那会儿的我,满脑子想的是兴趣而不是生活,也深刻认同“悟道未必出家,求学未必在校”这一观点,我为父亲写了一份游说他支持我辍学的信。

或许是因为我在里面大量描述了他作为数学老师完全没接触过的事,和我那有些“天真”的学习计划。或许是从初中那会儿我这样的。。。算是执着吧,父亲看了信的当下是答应我辍学的。一夜无话,第二天就反对了,理由也非常理性客观,而且是我无法反驳的,“风险太高”。因为无论是我还是父亲身边都没有通过这种方式真正给自己一口饭吃的人,所以我无法对这样的风险做出预估,就乖乖接着上学,考大学。

高考成绩出来的时候,别人选专业可能是比较费劲的,因为他们选的大多数是学校,然后看自己分数能选啥专业,然后再使用仅有的两三天事件快速浏览这个专业所对应的行业发展,就业等问题。用父亲的话说,我是比较容易的,对应着分数、软件工程专业找就行,虽然没能辍学成功,但是也算是成功从事了从很久以前就开始喜欢的行业吧,从走进大学的那一天,我的兴趣正式成为了我的生活。

“大学没有正经教会你什么”是我上完之后最明显的感触,大学给的不是教育质量,而是接触新鲜事务、认识自己平庸的机会,可能也正是这些机会,让父亲对于他的抉择更加有信心,“因为这样做,风险小”。在大学我认识了能让我奋斗一生的女朋友,我接触到了带我参加ACM的老师,让我加入工作室的老师,和我相似的许多人,发现了软件行业的各种职业测评。。虽然我唯一参加的一个还没过。。。发现了巨大的图书馆,发现了正儿八经做业务系统是多难,同时也发现。。。。。帮别人做做作业,讲讲课程设计是可以有经济收益的。

所以虽然不想承认,我的诸多技能是在大学期间积累下来的,也是我现在常用的一些技术,一些套路。从年龄上说,应该是在18-22岁期间,所经历的是大学生活。如果不经历这些,谁知到生活会怎么样呢?宝贝:“30岁结婚,40岁离婚,带个娃”。

万物皆IO

一切的程序都在不停的做一件事,那就是IO。对内存不断IO,对硬盘不断IO,对网口不断IO,对数据库不断IO,对项目中的最小代码单元(函数/成员方法)不断IO。总之,它就是在不停IO,这也IO组合起来就叫业务,它的唯一目的是为了应对业务场景。

通讯问题

一个项目的启动是要有具体业务的,没业务的项目我们称为技术DEMO,通常是自己学习使用。无论是收集需求还是研究同类型系统,都属于项目启动过程中非常前置的工作内容,我们暂且不讨论,当完全理解自己即将要做的业务场景时,我们首先要处理通讯问题,因为所谓的BS项目实质就是一种特殊的CS项目,由于C已经W3C标准固化为浏览器了,其实许多通讯方式也变得具体,如HTTP这样的应用层短连接协议,及时是大多数人认为的,不具备通用性的通信层协议TCP/IP,W3C组织也推了一个WebSocket的应用层长链接协议。这两个应用层协议是BS项目通讯的全部,他们对应两种大的具体的应用场景:

  • 场景中的大部分业务发起者是浏览器,如信息化系统,内容管理系统等,符合请求/响应模型,使用HTTP
  • 场景中的大部分业务发起者是服务器,如网页游戏,聊天室,实时地图等,需求服务器为浏览器主动推送数据,使用WebSocket
  • 场景中包含这两种情况,HTTP与WebSocket同时使用。

只有深刻全面的理解自己面对的业务场景,你获取才能正确的选择使用哪种通讯模式,因为通讯模式就是就是对IO方式的选择。如果你选HTTP,一般的项目团队可能会在着手写代码之前,写一份swagger定义,用于大概的描述业务过程。通常一份swagger文档的完成预示着前后端可以分开干活了,不过在项目的不断迭代过程中,这份swagger定义必然也会被改地面目全非,所以完全不需要纠结这份swagger要写的多么好,它存在的意义往往是让成员快点开始编码,修改前后端接口的入参和出参是很普遍的事情,这可能也是在后期两批开发人员最容易发生冲突的部分,特别是当这种修改无法避免的时候,如客户突然增加需求了,以前接口出的数据前端无法完成新的需求,而后端逻辑也需要更多的参数才能完成。所以在项目提出变更的时候,往往接口变的是入参还是出参,影响着到底是改前端代码还是后端代码。通常的项目会尽可能的只改一方,这样出了问题也比较好排查。

前端技术选型

首先我想区分一个概念,这个概念纯属个人看法,没有哪个行业标准明确地区分网站与Web应用,我个人的区分原则如下

  • 网站: 一种主要业务为内容发布的系统,用户主要的目的是浏览信息,交互频次较低。系统的核心竞争力是对搜索引擎的友好性,因为现在的内容发布性系统的主要访问量来源是搜索引擎,这也促使这样的系统必须极快的响应搜索引擎爬虫的请求,尽可能的在第一次请求过程中就返回足够总要的信息,爬虫的内容分析通常是静态的,即不予许JavaScript脚本执行,所以重要的内容信息通常是不允许JavaScript脚本二次请求。虽然这种系统中也存在二次请求的业务场景,但那大多数是为了提高用户体验而做的,这样的功能并非重点,所以这种系统的架构通常是MVC的。

  • Web应用: 一种主要业务与桌面软件类似的系统,用户通常会在某个页面上停留大部分时间,进行一些复杂的操作,交互频次较高。系统前端资源通常是纯静态的,也就是说无论数据库的业务数据如何变动,从静态资源文件到客户的浏览器上这一过程,后端是没有向前端资源嵌入业务数据的,所以对于内容做静态分析的爬虫来说,这个网站的“内容”是永远没有更新的,因为所有的业务数据都是通过JavaScript脚本二次请求的。对于开发者而言,这样的系统,其前端资源完全可以放在一个静态资源服务器中单独管理,这样的服务器只要硬盘IO够高就可以胜任。系统的开发模式也是前后分离的,也是这篇随笔重点向聊的内容。

目前行业中对于Web单页应用(SPA)的前端技术选型通常是选一种单页框架,如:Angular React Vue。对了,为啥把它们称为单页框架,而吧JQuery等称为库呢,我个人的理解是前者提供了一套完全的前端开发的解决方案,而后者更为灵活,用于解决前端开发过程中的部分场景。换而言之前者提供了表达逻辑的方法,更像艺术,而后者提供了解决具体问题的逻辑,更像工具。

就像艺术是因人而异的一样,众多的前端框架也是因人而异,无优劣之分。不过众多的框架都在努力做到以下几件事:

  1. 双向数据绑定。好像除了做特效,现在的大多数前端框架都在极力避免对DOM的直接操作,它即低效,同时也极易出错。转而提倡双向数据绑定,数据的修改即可完成对页面视图的更新,当然你也可以定制更新的方式,也就是强行操作DOM,这种应用场景对于系统开发来说很少,大多数的更新方式框架都已提供。

  2. 响应式编程。在框架中写代码,即使是简单的修改一个对象的属性,在框架的逻辑中也会处理绑定在它身上的所有监听逻辑,感觉所有的业务都写在监听器里,我们无需刻意的去等待一个值的变化,因为当这种变化产生式,框架回去主动触发你的代码。

  3. 单一状态树。随着以上两条的技术原则的不断发展,很多作者也开始思考前端开发的本质,最后他们发现实际上不止是前端,任何应用程序在自身内部都维护了一个特殊的数据结构,这个数据结构通常是树状的,用于表示这个应用在每个时刻的各种状态,以前我们或许会手动的将这些状态的变化渲染到视图上,让用户看见。现在做这一切变的简单的多,大量的库也层出不穷,如:Vuex、Redux等,他们固化的操作这颗状态树的流程,使得编程过程中更加不易出错。

单一状态树构造

读到这里,我想你应该已经为你的项目做好了以下两件事。

  1. 有一份针对业务的Swagger定义或其他类型的业务交互定义,明确系统的数据走向,最好有用例图或数据流图做指导。

  2. 选择一个学习成本不是很大的前端框架。

如果你准备好了,我们来做接下来的事。

无疑系统数据流图的端点是前端和后端,可能后端的节点要多很多,但是前端的节点通常只有一个。那么接下来就需要构造一个能表示前端视图在每个时刻状态的树了。

举个例子,比如我想在用户的跟人详情页面中显示自己的相关信息,昵称,性别,年龄等,我们就可以这么写这颗树。

let state = {
  userInfo:{
    userName:'zmp',
    age:'22',
    sex:'男'
  }
};

而后,我们需要在购物车页中显示商品的个数,商品的总价,商品的信息列表等

let state = {
  userInfo:{
    userName:'zmp',
    age:'22',
    sex:'男'
  },
  buy:{
    goodCount:12,
    MoneyCount:1314.0,
    goodList:[
      /*
      {
        goodName:'毛巾',
        count:1,
        money:12,
        url:'http://xxxx.xxxx/good/1001',
      }
      */
    ]
  }
}

可见随着我们的页面增加,或者说随着系统的功能增加,这颗状态树的节点也在不断增加,所以状态树的复杂程度取决于业务的逻辑复杂程度,而在状态树构造过程中,你可能还一句代码没有写,所有的内容都是自己想象的,这个阶段就是要让你大胆想象,尽可能的让这颗树全面,能够表达每个时刻的前端视图状态。

数据库设计

在我们构造好单一客户端的状态树后,我们就需要结核业务与客户端的状态树,来构建服务器端的状态树了。服务器也有状态,即使是使用HTTP这种无状态的,短连接协议,也无法摆脱服务器需要状态这个事实。而且服务器程序的实例很多时候不是一个,如大规模集群的情况,很明显就需要考虑状态一致性的问题。你不能把服务器状态像客户端一样写在内存里,因为进程的内存通常不共享,及时共享,代价也很大,更何况有些服务器程序实例还不是部署在同一台机子上的。

这里保存服务器状态的最佳实践无疑是数据库了,无论何种类型的数据库,你需要做的是保证每个请求过来,都能在数据库里查到状态(也就是业务数据)。如果你把客户端的那颗状态树写的足够全面,很多时候数据库设计的过程就是结合业务翻译客户端的状态树,通常是翻译成关系型的库表,当然也可以翻译成非关系型的。这主要取决于你选的数据库类型。

后端技术选型

如果系统不是互联网产品,通常复杂的开发过程往往是在后端。(如果是互联网产品,通常前后端都很复杂)因此后端技术的选型显的尤为重要,原因是有些业务数据不是自产自销的,即后端程序可能要整合别的什么系统,系统与系统之间走的通讯协议可能是长连接也可能是短连接,甚至有些项目中,后台程序是要整合某个开源项目的,所以我个人理解的,传统非互联网产品的BS项目,后端工作量更大。

严格意义上说,当你选择使用前后端分离的模式来做BS项目的时候,后端使用何种语言完全取决于你团队开发人员的技术方向,语言永远是用于表达逻辑的工具,及时是想C那种,在标准库中完全没有网络库的语言,也是可以针对不同操作系统,调用操作系统提供的网络服务来做很多出色的事情的,你如你看Nginx。

所以后端技术的选型语言不是最主要的,任何能操作网络的语言都是可以做后端程序的,这其中考量比较多的是后端程序与其他第三方系统的整合,最常见的第三方系统如数据库,大数据平台,当然除了这种偏技术类的存储服务,也有很多业务类型的服务,如人脸识别,语音识别,用户鉴权服务等。

后端开发过程中比较难受的就是有些服务不走网络,其提供的SDK对编程语言有要求,后端的技术选型主要的工作就是在收集编程资源。

业务开发

BS项目的业务开发,由于是前后分离的,前后的开发通常是并行的,且在此过程中也充斥着大量的联调测试,不过前后开发人员讨论的重点问题也会比较单一,通常是针对某个接口的入参出参格式的争论,此时就提现了当初那份swagger定义的重要性了,如果这一切(前端后端)的开发都是你一个人做,我也十分建议你先写swagger定义,这样能使你的代码修改受到限制,降低项目失败的概率。

总结与个人针对BS项目的技术栈

感谢你能听我唠唠叨叨到这里,我们总结一下前后端分离模式下的BS项目开发过程。

  1. 有一份针对业务的Swagger定义或其他类型的业务交互定义,明确系统的数据走向,最好有用例图或数据流图做指导。

  2. 选择一个学习成本不是很大的前端框架。

  3. 构造客户端的单一状态树

  4. 根据客户端的单一状态树设计数据库

  5. 选择一种适合后端系统做集成的编程语言

  6. 前后端同时开始编写业务代码

其中2与5可以同步进行,其余的步骤还是非常建议按顺序来的。

最后我来分享一下我个人处理BS项目时用到的主要技术栈。

  • 前端: Vue,Vuex,Axios/Socket.io,

有时也用electron来做桌面应用。

  • 后端: eggjs,各种eggjs的插件如:egg-sequelize,egg-socket.io等
  • 数据库: mysql
  • 缓存: redis

你可能感兴趣的:(前后分离结构的BS项目的一般开发流程)