我对架构的理解
什么是架构,从事的角度说,就是分层和分模块;从人的角度说,就是承担分层/分模块任务的角色。
不过目前社会上的架构师岗位,常常偏离了架构的概念,很多架构师岗位,不如改为基建工程师更贴切些。虽然基建工程师也需要全局视野和一定的架构能力,但他们的岗位职责并不是架构。
前端需要架构,但不需要架构师
说回前端,前端需要架构吗?
我的回答是:"前端需要架构的角色,但不需要架构的岗位"。只要写代码,就会有分层和分模块这类事情,那承担这个任务的人,就承担了架构的角色。
大多数前端项目在架构上都很简单,简单划分数据/视图/逻辑层就足以应付架构需求了,所以很多人觉得不需要架构,但实际上架构的角色扮演可能在你不知不觉中就完成了。
架构的意义只有在项目足够复杂时才能体现出来。
架构的意义只有在项目足够复杂时才能体现
比如svg画布编辑器(比如流程图),可能会有以下架构决策:

从图中可以看出主要模块的划分,并回答了关键问题------数据变更如何更新视图?
再比如前端领域常见的微前端架构,可能涉及到多个后端端服务(系统/菜单配置/统一用户中心等),cicd,前端子应用部署,基座应用公共能力设计等等,这些问题自然不可能边做边考虑。

日常前端项目中的架构实践
日常项目中我并不会按照通用的前端方法进行拆分,而是着眼于业务模块和基础机制。
以下是某项目的大致架构,左侧是技术架构,右侧是应用架构。整个项目代码量约20~30万行。 
左侧(技术架构部分)回答了业务模块/技术模块的划分,关键技术选型,重要问题的解决方案和重要组件的封装,部署/启动方式;并聚合其中部分模块成为独立模块/应用,(比如其中SimulationBuilder涵盖了整个建模业务,包括建模画布,SimulationCore包含了仿真运行和结果查看,都是相对封闭的小应用);最终的系统由不同业务子应用组成,业务子应用会依赖左侧的技术决策以及聚合出来的独立模块/应用。
这个架构是如何在实践中体现的呢?
根据模块划分约束文件夹或package。
业务领域划分的模块,对应到项目中,要么是一个大文件夹,内部聚合了整个模块的所有逻辑,要么是一个独立应用的npm包,要么是一个独立仓库,总之这个模块的逻辑只能写在这些规定的地方,并做到互不干扰,互不依赖,A业务领域的代码变动不能影响其他领域的,但可以通过依赖的基础业务代码而间接影响其他业务领域。
共享基础业务领域
基础业务领域负责通用业务,并被其他业务领域依赖。团队中每一个人,可以不清楚其他业务模块,但都需要清楚项目中有哪些基础业务,以及其中的设计方案/使用方式,禁止自创引用和静默更新。
遵循全局管理机制
全局管理中的所有模块,均不涉及业务逻辑,可以跨项目复用,并且有统一的规范,比如命名为xxMgr,统一采用面向对象方式编程,并提供实例管理(既可以是多例也可以是单例)。所有实例在项目入口创建和初始化。
全局管理往往是项目中的重要技术机制,是侵入项目最深的部分,不需要每个人都知道其中运行原理,每一个模块都有对应的维护人,所有的改动都需要和维护人讨论,尽量保持升级路线一致。
ui组件
ui组件中所列非第三方组件,都是非常重要的,可跨项目复用的基础组件,或可独立运行的黑盒业务组件,都需要封装为npm包,并有完备的文档。
跨模块通信
我将跨模块通信从全局机制中单独摘离出来。这一部分往往是项目中腐化最快最严重的模块。项目曾一度在面向对象和函数乱用之间跳转,因此我进行了严格的技术约束:
- 禁止使用redux/zustand等状态管理库;
- hox做零散只读数据和单点修改数据共享;
- 多点修改数据利用useSyncExternalStore封装为独立模块;
- 核心数据比如模型,抽象为对象结合observable更新视图;
- 跨层级组件方法调用通过事件通信。
依据架构文档做复杂设计
团队内当要改动某个复杂模块时,首先要拉出该模块先前的架构设计文档,判断出改动范围或改动方向。这一点能节省很多代码阅读和理解时间。
架构的渐进式演进
没有任何一个架构是一蹴而就的。我们项目最初仅是一个普通的spa项目,其复杂度主要是其中的SVG编辑器,一个工程就足够了。 但在后续的迭代中,我不断关注架构的适应性:
- 是否需要额外分层或之前的分层是否合理
- 模块是否臃肿或是否有必要拆除子模块
- 项目的编程范式或某些机制是否遭到了污染
- 是否要在宏观层重新划分
- 需要不容易解决的普适问题,是引入新技术还是对现有机制进行补丁
当嗅探到异样的代码味道时,我会标记->设计->公布升级路线->重构->验证->合并/公布结果,一方面不断改进划分合理性,另一方面确保团队内能大致明白目前的架构以及将来的发展方向。