# 前言

当人们讨论架构时,往往更多关注的是后端架构。因为后端的发展比较稳定,更注重代码实现。而前端的发展比后端晚了好多年。大概从Google Map在2005年使用了大量的Ajax之后,人们才意识到原来前端还可以这么做。随后,产生了单页面应用,并诞生了一个又一个的前端应用,直至出于解耦的目的,前后端也开始分离了。

对于只使用后端 API 的前端来看,后端看上去只做 CRUD(增加(Create)、读取查询(Retrieve)、更新(Update)、删除(Delete))。然而,后端并不像看上去那么简单。

从架构层面考虑,后端如何实现的?

主要考虑高并发、高可用、数据库瓶颈问题:存储的时候要考虑原子性、一致性、隔离性和持久性,使用的时候要考虑通过分表、存储、主从同步来提高性能和并发量,在这个过程中还要考虑备份、迁移、查询速度、效率等问题、代码实现上,使用消息队列来解耦依赖,使用微服务来拆分单体应用等。

那容易被忽视的前端架构又是如何实现的呢?

主要考虑可用性、性能、模型构建、组件复用、平台设定、浏览器兼容、交互设计、用户体验、移动端设计、桌面应用、物联网。

# 理解

说起软件架构,常常会想起盖房子。盖房子通常我们的做法是:

  • 地基设计建设,不然房子无法盖
  • 钢筋水泥浇筑,不然房子会歪歪扭扭
  • 后期维护,不然也会出现问题

那软件架构可借鉴在架构前需要规划、设计、实施和维护这几个阶段。而设计这个环节最重要的。

总结一下:没有最好的架构,只有最适合的架构。没有哪种架构能满足于下个时代的需求,因为架构是需要变化和不断改变的。其实,每个架构师都想设计出完美架构,不想被后来人吐槽。可现实往往是在设计过程中受能力、人力、财力、时间和环境等各种条件约束,所以最终的架构顶多适合并支持未来一段时间的扩展。所以在设计架构时,得抱着开放的平常新来看待架构设计问题。而后来者,要在熟悉架构后继续优化。

# 发展史

在我做前端开发时,前端还是没有框架的。通过操作DOM就能完成的工作,不需要复杂的设计模式和代码管理机制,也就不需要架构来支撑起应用。然而事情总不会一层不变。前端演进也涉及到重写、迁移、重构等情况。

  • 最初时期。由后端渲染出前端 HTML,用 Table 布局,用 CSS 进行简单的辅助,很多人都不理解了。
  • 动效时期。前端开始编写一些简单的JavaScript脚本来做动画效果,如轮播广告。
  • Ajax 时期。2005 年,Google 在诸多 Web 应用中使用了异步通信技术如 Google 地图,开启了 Web 前端的一个新时代。前端应用需要从后端获取数据,就意味着前端应用在运行时是动态地渲染内容的,这便是 Model(模型)UI 层解耦。
  • 动态 HTML。由后端返回前端所需要的 HTML,再动态替换页面的 DOM 元素。早期的典型架构如 jQuery Mobile,事先在前端写好模板与渲染逻辑,用户的行为触发后台并返回对应的数据,来渲染文件。
  • 模板分离。由后端用 API 返回前端所需要的 JSON 数据,再由前端来计算生成这些 HTML。前端的模板不再使用 HTML,而是使用诸如 Mustache 这样的模板引擎来渲染 HTML。
  • 构建工具。诞生了诸如 Grunt 和 Gulp 等构建工具。
  • ****。产生了用于前端的包管理工具 Bower 和 NPM。
  • 模块管理。也出现了 AMD、Common.js 等不同的模块管理方案。
  • 大前端。由前端来开发跨平台移动应用框架,采用诸如 Ionic、React Native、Flutter 等框架。
  • 组件化。前端应用从此由一个个细小的组件结合而成,而不再是一个大的页面组件。
  • 跨框架。在一个页面上运行,可以同时使用多个前端框架。
  • 应用拆分。将一个复杂的应用拆解为多个微小的应用,类似于微服务。
  • 遗留系统迁移。让旧的前端框架,可以直接嵌入现有的应用运行。

# 架构设计

  • 了解相关者需求。业务分析员安排项目的迭代计划、产品负责人关心是否能按时上线、项目经理决定项目所需资源、技术负责人对架构设计和演进、测试人员的测试策略以及高层领导的组织战略。
  • 技术讨论、确定可行性技术方案。需要从用户体验、性能、安全、代码维护、平台化等方面着手考虑,不同项目侧重点也会不同,所以定好优先级和侧重点对后续工作很有帮助,减少设计上的返工。
  • 梳理功能需求。这里包括系统或软件应有的可见的功能,另外还有一些隐性的功能,比如可用性、可维护性、可变性、容错性、可升缩性、浏览器兼容和移动端支持版本等。
  • 罗列技术风险点。系统风险部分,才是最影响应用开发的。包括技术风险、与第三方系统集成、运行环境和时间风险等。
  • 方案确认。比如架构风格划分:
    • 分层风格(将系统水平切分为多层,每层有多个模块组成,每层使用下层功能并为上层提供服务)
    • MVC 风格(模型 Model、视图 View 和控制层 Controller,视图和控制器完成用户界面,并设计一套机制保证用户界面和模型一致)
    • 发布-订阅风格(基于事件,一个对象发生变化,所有订阅它的对象都会收到通知,实现代码上的解耦。发布者只需要在适当时候发布事件无需关心事件订阅者,而订阅者只依赖于事件即可)
    • 管理和过滤器(适合于处理数据流,每个步骤封装在过滤器组件中,数据通过相邻过滤器间的管道传输,比如 UNIX shell)。这块偏后端架构比较多,但是发现没有,这似乎和 Javascript 设计模式很熟悉。前端架构可以借鉴后端架构和设计模式来设计。
  • 设计方法
    • TOGAF(The open group architecture framework 开放组体系结构框架)包含业务架构(定义业务战略、治理方法和关键业务流程)、应用架构(各个应用程序部署蓝图,展示它们的交互及与核心业务流程的关系)、数据架构(描述了一个组织的逻辑、物理数据资产及数据管理资源的架构)和技术架构(定义了支持部署业务、数据和应用程序服务所需的逻辑软件和硬件功能,它包含了IT基础设施、中间件、网络、通信、处理和标准),适合于大型企业。
    • ADM 方式有4个阶段:架构上下文(预备阶段和架构愿景)、架构交付(业务架构、信息系统架构和技术架构)、迁移计划(机会及解决方案和迁移计划)和架构治理(实时治理和架构变更管理和持续监控),适合于中小型企业。
  • 细节细化。在进入开发阶段前,要准备好:架构图(整体架构和架构系统间的关系)、迭代计划(按照业务和技术的要求,按时间顺序排列出项目的实施计划)、技术栈及选型(确定项目中使用的语言、框架、库等相关的技术栈,以及相应的依赖等)、示例代码(在这些代码中展示架构的风格及相应的设计规范)、测试策略(明确项目的测试类型、测试流程,以及相应的人员在哪些层级进行测试)、部署方式(定义应用的部署方式和方案)
  • 需求排期

# 层级设计

面向不同级别的人时所展示的内容也是不一样的。如我们作为一个架构师、技术人员,在面对同一级别、更高一级别的架构师、技术人员时,说的便是形而上学的东西,如微前端、前后端分离等各种概念。

# 坚守原则

  • 刚刚好:不缺少关键部分,不多加多余设计。过度设计很多都是假想出来,会加大项目成本,也会使得参与人数过多。设计不足会使架构扩展性不强,不能灵活地应对变化。
  • 持续演进:长期的架构改进比什么都重要。随着代码的增多,架构也在不断地变化,也需要不断地被纠正,以符合现有的需求。团队成员需要在项目中进行相应的工程实践,日常的开发中,使用敏捷方法实践相关的持续集成、持续部署。除了业务应用本身,互联网应用还需要一系列的配套服务日志、用户行为日志、性能监控等。如果以互联网公司的思维来看,这些事件都不会是一蹴而就的,“先上线,后解决问题”才是真理。在项目开始阶段,某些部分可以手动来完成。然后,在项目演进的过程中不断开发相应的功能,以满足自己的需求。如果出于人力、物力所限,设计的架构不够合理,也没关系——人的能力是在不断提升的。若是有机会回到之前的场景,再慢慢思考这个问题,仍然有可能感觉当时的方案是最合适的。
  • 延迟决策。如果架构上有多个可演进方向,无法做一个合适的决策,那么可以在条件更加充分的时候再做决策,而不是花费大量的时间盲目地修改架构,那样只会造成资源浪费。