引言:“架构”是前端开发中一直以来都缺少的。由于近几年Web应用日趋复杂,前端架构开始流行起来。成熟的工具使得开发人员可以针对要解决的问题设计出可扩展的架构。
构建可扩展的软件,可以从很多角度来思考软件架构。但是如果每个角度都去考虑,根本不可能做出想要的软件。这就是为什么需要从架构的角度对设计进行取舍:取我们最需要的,舍次要的。
本文选自《大型JavaScript应用最佳实践指南》。
确定不可变内容
在做出取舍之前有一点很重要:列出那些不能舍弃的需求——我们的设计的哪些方面对实现可扩展是至关重要的、不能改变的。比如,被渲染页面中的实体个数或者函数间接调用的最大深度就不能改变。虽然不可变的内容不会太多,但是它们确实存在。最好的办法是缩小这些内容的作用范围,减少它们的数量。如果有太多严格的设计原则不能被打破或改变以迎合需求,就不能更好地适应不断变化的可扩展性影响因素。
考虑到可扩展性影响因素的不可预测性,无法改变的设计原则是否还有意义?答案是肯定的,但只有在它们浮现出来,并且显而易见时才成立。所以这可能不是前期的原则,虽然我们经常要遵守至少一两个前期原则。这些原则可能是从早期代码重构或后期软件的成功中总结出来的。在任何情况下,这些不可改变的内容必须明确下来,并和所有相关人员达成一致。
从开发的便捷性考虑性能
性能上的瓶颈需要在一开始就被修复或避免。一些性能瓶颈是显而易见的,会对用户体验造成明显的影响,这些就需要立即修复。因为这些瓶颈意味着我们的代码由于某些原因无法扩展,并且可能引出设计上更大的问题。
其他性能问题相对较小,通常是开发者为了通过各种手段提高性能,对代码进行基准测试时发现的。这些无法很好地扩展,因为这些小的性能瓶颈无法被用户察觉,但是修复起来非常耗时。即使应用程序大小合理,又有不少开发人员,但是每个开发人员都在修复这类小问题,就无法继续开发新的功能。
一方面,这种细微的优化会引入对特殊情况进行处理的代码,而这类代码对于其他开发者来说就没那么容易理解了。另一方面,如果不进行这种细微的优化,代码就会相对简洁,容易维护。在必要时,需舍弃性能优化来保证更好的代码质量。这样才能增强我们在其他方面提高可扩展性的能力。
性能的可配置性
如果有几乎每个方面都可配置的通用组件自然是极好的。然而,设计通用组件的代价需要牺牲性能。这在一开始只有少量组件时是无法察觉的,但是当软件功能、组件数量、组件配置项开始增加时,问题就显现出来了。随着每个组件尺寸(复杂度、可配置项的数量等)的增长,组件的性能就会呈指数递减,如下图所示。
只要性能问题没有影响到用户,就可以保留配置选项。不过需要注意的是,可能会在消除某个性能瓶颈时不得不删除某些可配置项,不过可配置项不太可能成为性能瓶颈的主要来源。在不断扩大和增加功能的过程中还容易过于投入,回顾起来你会发现,在设计时创建的自认为有用的可配置项,最终并没有什么用,反而加大了开销。所以,当配置项没有带来确凿的好处时,为了保证性能,应该果断舍弃。
从可替换性考虑性能
一个与可配置性相关的问题是可替换性。现在我们的用户界面运行良好,但是随着用户数量和功能的增加,我们发现某些组件无法轻易地被另一个组件替换。这可能是一个软件成长问题:想设计一个新的组件用来替换已有组件,或者可能需要在运行时替换某些组件。
替换组件的能力基本上由组件通信模型来决定。如果新的组件可以像已有组件那样发送/接收消息/事件,那么替换起来就相当简单了。但是并不是软件的所有方面都需要可以替换,为了保障性能,可能根本没有可替换的组件。
但当扩展应用时,可能需要考虑将大组件重构为较小的可替换组件。但是这样做会引入新的间接层,从而影响性能。不过牺牲一点点性能换来可替换性,可以帮助我们在其他方面获得架构的可扩展性。
可寻址性的开发便捷性
为应用程序中的资源分配可寻址的URI 必然会增加功能实现的难度。真的需要为应用暴露的每个资源分配URI 吗?也许不需要。从保持一致的角度看,为每个资源分配URI是合理的。但是如果针对某些资源,没有路由以及统一、易于遵循的URI 生成方案,那么更倾向于不为这种资源分配URI。
为应用中的每个资源分配URI 带来的负担是值得的,因为不支持可寻址资源更糟糕。URI 让我们的应用和用户熟悉的其他页面表现一致。也许URI 的生成和路由在应用中是不可改变的、不可舍弃的,所以几乎总是牺牲开发的便捷性来换取可寻址性。相较于URI,开发的便捷性可以随着软件的成熟更深入地解决。
性能的可维护性
功能开发的难易程度最终取决于开发团队和可扩展性的影响因素。例如,可能出于预算的压力不得不招聘初级开发人员。这样是否能适应后期要求,则取决于代码。当需要考虑代码性能时,很可能会引入一些对于缺乏经验的开发者难以理解的代码。很显然,这会阻碍新功能的开发。如果新功能开发难度比较大,那就会花费更长的时间,这显然不能适应用户的需求变化。
开发人员并不是总需要费力理解这些用来解决特定领域性能瓶颈的晦涩代码。当然可以通过编写高质量、易于理解的代码,甚至编写文档来缓解这个问题。但这一切都是有代价的,随着团队的壮大,需要短期内完成对开发者的培训、指导,这些都是在生产效率方面需要付出的代价。
在关键代码上经常是优先考虑性能而不是开发的便捷性。不能总是逃避代码性能带来的代码丑陋,但是如果这些丑陋的代码能很好的被隐藏,就可以得到更易理解和自解释的代码。比如,底层JavaScript 库性能良好,API 紧凑易用,但是如果你看一下底层的源码,就会发现并不是那么优美。这就是我们的收获——让别人维护出于性能原因而看起来丑陋的代码。
减少功能以提高可维护性
当所有其他手段都失败时,需要退后一步,全面审视应用中的所有功能。架构是否能够支持所有的这些功能?把投入了大量时间来开发的架构废弃掉是毫无道理的,但也确实会发生。大多数时候,会被要求实现一组颇具挑战的与我们结构相悖的功能。
这种情况发生时,实际上是在打乱现有的稳定功能,或者在应用中加入一些低质量的东西。两种情况皆无益处,但值得投入时间,花费精力,与投资人沟通以决定哪些必须被删除,尽管这个过程并不愉快。
如果已经做出折中的选择并且花时间理清了我们的架构,那么应该有一个合理的理由来解释为什么我们的软件不能支持上百个功能。
利用框架
框架的存在是为了通过采用一套紧密聚合的模式,帮助我们实现架构。框架具有极大的多样性,选择哪个框架是由个人偏好以及与设计的契合度来决定的。例如,一个JavaScript应用框架可以实现更多创造性,而另一个框架拥有更多功能,但其中大部分功能是我们不需要的。
JavaScript 应用框架有不同的大小和成熟度。有些内置了很多套件,有些则倾向于机制而非政策。没有一个框架是专为我们的应用设计的,对于每个框架所声称的功能要持有存疑态度。框架标榜的功能只适用于简单的一般情况,而在我们架构中的应用则完全是另外一回事。
尽管如此,当然可以用一个自己喜欢的框架作为设计过程的输入。如果我们真的喜欢这个工具,团队也有使用经验,可以让它来影响决策者。只要我们明白,框架不会自动地响应扩展影响因素,因为这个部分是由我们来负责的。
本文选自《大型JavaScript应用最佳实践指南》,点此链接可在博文视点官网查看此书。
想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。
相关推荐
这种方式有助于提高代码的复用性,降低复杂度,提高系统的可维护性和可扩展性。模块化架构是目前大型Android应用开发的趋势之一。 2. 微信架构历史回顾:文档中提到的微信-IALoEA架构的演变,从简单分层架构、多...
- **架构设计原则**:这部分着重描述了在设计过程中考虑的重大权衡取舍,如性能与可扩展性之间的权衡。这些原则对于指导整体架构设计至关重要。 - **备选架构设计方案及被否原因**:通过对备选架构设计方案的描述...
抽象思维能力使他们能够将复杂的问题简化为可管理的模块,而逻辑思维则帮助他们设计出高效、可扩展的系统架构。这种能力不仅体现在技术设计上,还包括对业务需求的理解和转化。 【技术前瞻性】 保持技术前瞻性是...
2. **可伸缩性**:随着用户量和数据量的增长,架构能够通过添加更多的资源来扩展,而不会降低服务质量。 3. **高性能**:提供快速的响应时间和低延迟,以满足用户的期望和需求。 在传统的关系型数据库事务中,遵循...
- **核心价值**: 优秀的软件架构能提升软件的健壮性、可扩展性、可伸缩性、适应性及稳定性,有助于复用已有的开发成果,减少重复工作,从而保护软件开发商和用户的利益。 #### 二、软件架构的研究方向 - **主要研究...
Google的成功案例表明,通过线性可扩展的架构设计,配合硬件的扩展,可以有效地满足增长需求。然而,中国的网络环境,如网通和电信之间的差异,也需要在架构设计中予以考虑。 运营成本也是架构设计不可忽视的因素,...
CAP理论指出,在分布式计算中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)三者不可兼得,必须有所取舍。传统数据库系统通常保证ACID模型下的强一致性和高可用性,这使得...
10. 平衡多方利益:架构设计要考虑多个利益相关者的诉求,包括性能、扩展性、维护性等。 11. 草率提交的危害:避免仓促完成任务,确保每个决策都经过深思熟虑。 12. 以业务目标为导向:所有决策都应服务于业务目标...
大型网站架构是互联网技术领域的一项重要研究课题,它涉及到如何构建一个能够处理高并发访问、高性能、高可用性、可扩展性及安全性的网站系统。大型网站架构的设计目标主要包括高性能、高可用性、伸缩性、扩展性和...
2. 分布式系统特性:包括透明性、容错性、可扩展性、高性能和资源共享等。透明性使用户感觉不到系统是由多个部分组成的,而容错性则保证了即使部分组件故障,系统仍能正常运行。 3. CAP定理:在分布式系统中,一致性...
22. 取舍的艺术:架构设计需要权衡,不可能满足所有需求,要懂得适时妥协。 23. 打造数据库堡垒:从项目开始就重视数据模型的设计,确保数据的安全和一致性。 24. 重视不确定性:利用不确定性来推动创新,不必急于...
4. **高可靠性和可扩展性的架构设计**: - **系统容错性**:面向错误进行设计,确保服务无状态,当出现故障时,能够自动替换。 - **实时监控**:通过实时监控程序运行状态和热点,及时发现并隔离异常服务,确保...
此外,框架应该具备统一的数据协议、可插拔和可扩展的计算资源、持久化机制、多语言互操作以及便捷的编程模型。例如,RPC对话和Map-Reduce批处理模型分别适用于实时交互和批量处理场景。 运行态(平台)的关注点...
#### 一、高性能与高流量系统架构设计的基本原则 在构建高性能与高流量的系统时,基本原则至关重要。这些原则包括: - **以失败为前提进行设计**:任何软件都无法做到完全无bug,硬件也无法做到绝对可靠。因此,在...