在你刚刚成为一个开发团队的新成员,接到一项激动人心的任务:开发一款全新的产品。这个产品的背后是一个复杂的系统,意味着在正式动手之前,深思熟虑的架构设计是必不可少的。你面对着一块空白的白板,准备在上面勾勒出你的设计。问题来了:你该从哪里开始呢?如何确保你的架构设计既合理又高效?
我们知道,在软件架构的世界里,没有一套固定的规则可以遵循。这就像没有一个确切的方法可以指导你赢得所有的争论,或者教你如何写出一本人人都爱读的书一样。然而,这并不意味着我们完全是在黑暗中摸索。事实上,有很多实用的工具和策略可以指引我们前行。
我想与你分享一些在设计软件架构时非常有用的技巧和最佳实践(如图9-1所示)。这些建议可能比如ATAM这样的现有方法来得更加灵活和实用,它们不那么依赖于严格的规则,而是更加注重于实际效果和快速迭代。这些建议是基于我个人在参与多个大型咨询和工程项目中积累的经验,虽然它们带有一定的个人主观色彩,但它们都是经过实践检验的有效方法。
1.九大设计技巧

1.1先从抽象组件着手,别急着画部署图
在软件架构的世界里,刚开始接触时,很多开发者往往会立刻投入到具体技术的选择中去,思考如数据库、虚拟机、Web服务器、消息队列、云计算平台等这些技术构件。这种方法虽然看似直接面向问题,但实际上,这些决策应该是建立在一系列更深层次的架构设计和行为模式决定之后。此外,加入第三方组件不仅会为系统增添更多的复杂性,也可能在项目初期就不必要地分散了我们的注意力。
因此,更为明智的做法是,从更高层次、更抽象的角度去构建软件架构。正如UML(统一建模语言)通过其组件图推荐的那样,最初应当使用抽象组件和概念来定义你的架构。比如,考虑到系统可能需要一个支付处理模块,你可能会想到它应该支持异步操作,也许还需要维护某种形式的持久状态。以这样一种抽象的方式来描述系统的各个方面,不仅为后续的具体技术方案选择提供了一个稳固的起点,而且这些最终的技术决策将在很大程度上依赖于功能性和非功能性需求,以及那些可能尚未被发现的组织和技术限制。
当设计讨论过早地转向具体技术,例如"让我们使用MongoDB作为数据库,部署到云服务上,使用C#作为开发语言",这样的讨论可能忽视了对项目整体需求和潜在约束的深入理解。在软件架构设计的初期,深入探讨和理解抽象概念和组件是至关重要的。这种方法不仅可以帮助我们避免过早地局限于特定的技术栈,而且还能确保整个架构设计过程更加注重于满足业务需求和应对未来的挑战。
软件架构的设计应该以对系统的深刻理解为基础,而不是从一开始就决定使用特定的技术解决方案。通过这种方式,我们可以构建出既灵活又可持续发展的软件系统,为满足当前和未来需求提供坚实的基础。
1.2别急着选模式
在软件开发的世界里,设计模式是一把双刃剑。它们能够提供解决问题的经典方法,比如MVC(模型-视图-控制器)、管道和过滤器、领域驱动设计(DDD)、命令查询责任分离(CQRS)、端口与适配器、事件溯源等。这些模式在构建软件组件的过程中,能够作为非常有价值的参考和框架,为我们指明方向。然而,如果在设计初期就过分依赖这些模式,可能会导致系统设计不必要的复杂化,或者陷入炒作驱动的开发,追求那些看似前沿但实际上并不适合当前项目需求的技术。
设计模式的真正价值,在于它们提供了一种解决特定问题的方法论,而不是一成不变的规则。在我们决定使用某个模式之前,首先应该是对整个软件系统的顶层设计有一个清晰的理解。这就要求我们先从宏观上把握整个项目的结构和需求,理解不同组件之间如何相互作用,以及它们在整个系统中扮演的角色。换句话说,我们需要先了解整个"森林",而不是急于去研究树木的细节。
每种系统设计模式都有其适用的场景,优点和局限性并存。重要的是要认识到,软件系统是多样化的,其不同部分可能需要采用不同的设计方法来满足特定的需求。盲目地追随某个模式,不仅可能会导致资源的浪费,还可能使得最终的产品难以适应未来的变化和扩展。
在我们考虑将某个系统设计模式应用到项目中时,应该基于对项目整体需求的深入理解和分析。我们需要评估这个模式是否真正适合解决当前面临的问题,是否能够帮助我们更好地实现项目目标,以及在实际应用中是否可行。这种评估和选择的过程,需要我们保持批判性思维和灵活性,而不是简单地追求技术的新颖性或流行度。
系统设计模式是工具,而不是目标。我们应该根据项目的具体情况,有选择性地利用这些工具,始终将满足项目需求和目标作为首要考虑。通过这样做,我们可以避免过度设计和炒作驱动的开发,确保软件架构的合理性、有效性和未来的可扩展性。
1.3初次设计不要期待过高
在软件架构设计和项目规划领域,有一个共通的经验法则:初次尝试很难直接达到完美。尤其是在项目的起始阶段,当我们对未来将遇到的技术挑战和非技术障碍的认识还相对模糊时,试图锁定最终的架构或者制定详尽的项目计划,就像是在未知的海洋中航行,既充满勇气也充满了不确定性。因此,采用迭代的工作方法不仅是可取的,更是必要的。这意味着我们的架构设计和项目计划应随着对系统理解的深入而不断演化。
在项目初期,我们对整个开发旅程充满了期待,但同时也面对着众多未知。这个时候,如果我们试图定义一个固定不变的最终架构或者一份详细到每一步的项目计划,很可能会在后续的开发过程中遇到问题。随着项目的推进,新的技术信息、用户反馈和业务需求的变化会不断涌现,这些都可能要求我们对初期的架构或计划进行调整。
更好的做法是,让我们的架构设计和项目规划具有生长和适应的能力。在项目进行的每一个阶段,我们都应该积极收集和分析信息,基于最新的系统知识来优化和调整我们的计划。这个过程就像是在绘制一幅逐步清晰的地图,开始时我们只能依赖于大致的轮廓和方向,但随着探索的深入,每一处细节都会逐渐显现,使我们能够更加精确地导航。
通过这样迭代和逐步深化的方式,我们可以更加灵活地应对开发过程中出现的各种挑战和变数。这不仅有助于提高项目成功的可能性,还能确保我们的架构设计和项目规划能够紧密地贴合实际的业务需求和技术条件,从而构建出真正有效、可持续发展的软件产品。
1.4创建对功能需求的顶层视图
功能需求是软件开发过程中的关键组成部分,它们定义了软件系统必须提供的各项功能。通常情况下,产品负责人和领域专家会利用用户故事来详尽捕获这些需求,用户故事详细描述了参与者、必要条件和可能的功能流程等信息。这种方法有助于确保开发团队能够全面理解功能的预期行为和业务目标。然而,在项目设计的初期阶段,深入到每一个细节,比如掌握所有业务规则、用户故事或系统领域的方方面面,可能并非必要,甚至会过于繁琐。
相对于深挖每个细节,开始阶段更推荐从一个宏观的视角来理解功能需求。这时,构建一张反映应用领域关键名词的思维导图成为一个极佳的起点。这张导图应该突出显示应用领域内最重要的名词,并尝试按功能主题将它们进行分类。接下来,围绕这些核心名词,标注出最关键的动作或动词。这种方法不仅能帮助快速把握系统的功能结构和复杂性,还能促进对潜在设计方案的思考。

如图10-2所示的思维导图,这种高层次的功能需求梳理对于评估不同设计方法的适用性极为重要。例如,在面对一个复杂且多变的业务领域时,领域驱动设计(DDD)可能是理想的选择,因为它专注于深入理解业务领域,以此来指导软件的设计和开发。这种方法能够有效应对复杂业务逻辑的挑战。然而,对于那些业务逻辑相对简单、主要围绕一些基础数据操作和转换的场景,采用DDD及其相对复杂的实践可能就显得有些过头了,容易导致设计阶段的过度设计,产生许多不必要的中间层和抽象,从而增加了系统的复杂度而非简化开发。
在项目早期阶段,以一个更宏观、更灵活的方式来理解和组织功能需求,不仅有助于保持设计的简洁性,还能为选择最合适的设计和开发策略提供坚实基础,从而避免不必要的开发复杂性,确保最终软件产品的质量和可维护性。
1.5仔细识别非功能性需求
非功能性需求在软件架构设计中发挥着至关重要的作用,它们涉及到系统的质量特征,而不仅仅是功能本身。我们常见的非功能性需求包括:
- 性能:指系统的响应速度和处理数据的能力,强调快速响应和高吞吐量。
- 可伸缩性:描述系统应对增长工作量的能力,确保在负载增加时系统依然能够高效运行。
- 可移植性:系统能够在不同的平台或环境中运行的能力,保证了系统的灵活性。
- 合规性:系统遵循特定行业标准或政策的程度,对于确保法律和规范要求至关重要。
看到这些质量属性,我们可能会本能地希望自己的系统能够全面拥有它们。然而,在实际的软件开发过程中,选择合适的非功能性需求集合显得尤为重要。原因在于,每一项非功能性需求都可能增加设计和实现的难度,并且在不同的需求之间很可能存在冲突。例如:
- 性能与可移植性的冲突:一个高性能的系统可能需要依赖特定的硬件特性,这限制了它在不同环境下的可移植性。
- 可伸缩性与性能的权衡:在设计系统以支持可伸缩性时,引入的额外抽象层可能会降低其处理速度,影响整体性能。
- CAP定理的挑战:在分布式系统设计中,CAP定理揭示了一致性、可用性和分区容错性三者之间的基本权衡,指出很难同时满足这三个条件。
这些例子强调了在设计过程中需要做出的精细权衡,提示我们在确定软件架构时必须仔细考虑和选择关键的质量属性。为了确保项目的成功,建议将这些非功能性需求详细文档化,并与项目的所有利益相关者进行充分讨论。这样不仅有助于明确项目的重点和方向,还能够确保最终的架构设计能够满足核心的业务需求,同时平衡各种技术和质量上的挑战。
1.6 注意非功能性需求的范围
在软件系统的设计过程中,非功能性需求扮演着至关重要的角色,它们描述了系统的质量特性,如性能、可伸缩性和可移植性等。然而,并非所有非功能性需求都需被视为影响整个系统的核心质量特性。有时,这些需求仅限于影响系统的特定部分,这一发现对于管理因应非功能性需求而引入的实现复杂性至关重要,因为这样的复杂性可以被有效地局限在特定的范围内。
在项目开发的某个阶段,你会与产品专家和其他相关利益方讨论潜在的非功能性需求。当听到"基于Y原因,我们的系统必须具备X特性"这样的论断时,关键在于细致地分析Y原因是全面适用于整个系统,还是仅针对系统的某个特定区域。实践中,往往会发现,这些原因多数情况下仅与某些非常具体的情况或用例密切相关。
例如,某位利益相关者可能会提出:"鉴于我们需要聚合大量数据点以生成报告,我们的系统必须能够支持非常高的数据处理量。"对此,直接将"性能"定位为整个架构设计的顶级目标可能并非最佳策略。相反,应当将这一需求更精确地局限于具体的应用场景中。换言之,这个需求应该被译为:"我们系统中的一个特定组件,负责数据聚合,需要能够处理极高的数据量。"
这种精确的需求定义使得我们可以将解决方案集中在该数据聚合组件的设计上,可能采用某种异步处理机制。只有在这个特定的组件中,才真正需要考虑如何实现高吞吐量,比如通过特殊的数据存储解决方案或采纳如CQRS或事件溯源等高级设计模式。
在探讨非功能性需求的同时,对系统的功能需求进行顶层可视化分析极为有益。这不仅能帮助利益相关者更全面地考虑每个功能集群可能关联的质量特性,还能促进一种更为细腻和范围敏感的讨论氛围,避免直接跳入对具体技术组件的讨论。通过这样的方法,可以确保非功能性需求的讨论更加聚焦于实际需求,同时减少不必要的设计复杂性,提高系统设计的整体效率和实用性。
1.7确定重要的功能需求
我们探讨了软件开发中两个关键类型的需求:功能性需求和非功能性需求。功能性需求关注软件应该"做什么",即软件系统的具体行为和用户的使用场景。而非功能性需求则是描述软件"怎么做得好",它包括软件的质量特性------如性能怎样、是否易于移植和扩展等,以及需要遵守的技术限制。简单来说,功能性需求定义了软件的职责,非功能性需求确保软件在执行这些职责时的质量和效率。
作为架构师,我们的核心任务是设计并构建出既能满足特定功能需求,又能符合预定非功能性标准的系统。具体来说,这涉及到两个关键要求:
- 能够启动并支持所有预定的功能需求,确保系统具备必要的功能和操作,满足用户的具体需求;
- 同时,还要遵守所有既定的非功能性要求,包括但不限于系统的性能、可靠性、可扩展性等方面,确保系统不仅能做到,还要做得好。
在设计过程中,我们必须面对各种各样的挑战,其中不乏组织约束和风险因素。但在这一切之中,用户需求始终是我们关注的核心。它们不仅指明了我们寻求解决方案的方向,更深层次地影响着我们定义问题的方式。实际上,用户需求就像是设计过程的指南针,帮助我们在复杂的设计海洋中找到正确的航向。
在先前讨论的技巧1.5中,我强调了非功能性需求,如系统的稳定性、安全性和响应速度,对于系统架构设计的巨大影响。那么,关于功能性需求,我们又该如何考虑呢?功能性需求描述了系统必须执行的具体任务,比如数据处理、用户交互等,它们对设计的影响又是怎样的?
不是所有的功能需求都对设计有着等同的影响力。然而,系统的功能特性对其结构、技术选择和最终的行为有着不可忽视的影响。例如,一个为分布式人力资源管理而设计的平台,其架构与一个用于控制工业输送机的系统截然不同,而这两者与游戏引擎的设计又大相径庭。
功能需求往往直接引导我们做出某些设计和技术决策。如果忽视了这些需求,某些功能可能变得难以实现,或者只能通过牺牲其他方面的表现来勉强实现,这样不经意间就增加了系统的复杂性。
以一个较为简单的例子来说,一个基于数据表单和报告的应用很可能会选择使用关系数据库。尽管我们可以采取其他技术手段,比如基于文件的I/O操作,但一旦涉及到数据的聚合或者需要进行复杂的数据查询,我们就不得不实现那些数据库"开箱即用"的数据管理功能。这样不仅增加了实现的难度,也可能因为技术选择不当而引入了不必要的复杂性,正如Fred Brooks在其著名的《No Silver Bullet: Essence and Accidents of Software》论文中所讨论的"偶然复杂性"。
通过对功能需求的深入分析,我们可以识别出一组由相似设计考虑驱动的需求集合。这不仅有助于我们更有效地解决问题,还可以避免在解决方案设计中引入不必要的复杂性。这种方法允许我们在确保系统功能完整的同时,也最大限度地简化系统架构,确保技术选择的合理性。这样的设计思路,不仅提高了开发效率,更重要的是,能够提升系统的可维护性和可扩展性,为未来的需求变化和技术升级留出充分的空间。
在设计流程中,深入理解和分类功能需求是至关重要的步骤,它有助于我们将设计理念转化为实际可行的解决方案。这些需求往往不是孤立存在的,而是按照它们背后的设计思想聚集成群。以一个具体的CRM应用程序为例,假设其功能需求包括:
- 数据表单能够支持自定义颜色、字体和品牌图像,以便在视觉上与组织的企业设计保持一致。
- 数据表格的必填字段通过柔和的色调和阴影来突出,以提高用户体验。
对于上述需求,如果我们设计出一个框架,能够灵活地支持第一个需求的各种自定义视觉元素,那么第二个需求对整体设计的影响相对较小。这是因为两个需求都指向了同一目标:一个能够提供丰富用户界面和视觉定制能力的系统。
在分析这些功能需求时,我们的目标是挑战现有的设计理念,不断寻找优化的空间,确保能够以最佳方式实现这些功能。这并不意味着在设计的早期阶段就必须锁定所有的实现细节。但是,对于那些可能挑战我们现有架构和技术选择的需求,我们需要保持警觉,避免设计决策陷入僵化。
这个过程本质上是迭代的,需求和设计理念会随着项目进展而不断演化。因此,持续地跟踪需求的最新动态和理解这些需求如何影响架构设计变得极为重要。
一个有效的做法是对需求进行初步的标记,使用关键词来指示可能的解决方案方向或涉及的设计考虑因素。这些关键词可以是概念性的,比如图数据库、推送通信、报告引擎、丰富的UI、实时操作系统、GPU加速等,这样做的目的是在不局限于具体技术的同时,提供一个广阔的设计视角。
通过标记需求,我们能够轻松识别出依据相似设计思想或技术需求聚集的需求群。接下来,选择每个群体中最具挑战性的需求作为深入讨论的焦点,与开发团队和利益相关者共同探讨。这种方法不仅促进了解决方案的迭代完善,还为我们做出的设计决策提供了明确的记录和合理的解释。
随着对功能需求和解决方案概念的理解加深,我们可以进一步深入到技术细节,开始规划技术栈的构建。值得强调的是,整个设计过程应该被视为一个持续迭代和发展的旅程,而非一套固定不变的规则。这种灵活、开放的设计思维方式,使得我们能够适应需求的变化,同时也为未来可能的技术进步留出空间。
1.8定义质量属性场景
在软件开发的第1.5节内容中,我们探讨了非功能性需求,这些需求主要分为两类:技术限制和质量属性。质量属性包括但不限于系统的可用性、可扩展性、可移植性等方面,它们在整个软件设计和开发过程中扮演着至关重要的角色。
谈及技术限制时,这些通常指我们在设计解决方案时必须遵循的具体要求,如必须集成特定的系统、采用特定的编程语言或确保与某些特定环境的兼容性。这些限制对于我们的设计方案有着直接而明确的影响,它们定义了我们可以采取的技术路径和边界。
然而,当我们深入到质量属性的分析时,挑战就变得更加复杂和有趣了。首先,我们面临的挑战是如何精确地定义这些质量属性。根据我的经验,相较于其他需求,质量属性的描述往往是最模糊不清的。在许多项目中,这些质量属性需求可能仅仅是口头上的提及,很少会有详细的书面记录。尝试将它们具体化时,你可能会遇到一些非常模糊的描述,例如:
- 应用程序的性能至关重要,我们需要确保用户有一个流畅的体验。
- 系统必须具备良好的可扩展性,以便我们能够轻松地适应未来的新需求。
- 对于我们的成功来说,应用程序的可靠性是不可或缺的。
尽管质量属性如可扩展性、可靠性等有其标准化的定义,但在实际应用中,它们为不同的解释提供了广泛的空间。正是基于这一点,在技巧1.6中我提到,讨论这些质量属性的具体范围非常有助于我们深入理解它们背后的想法和动机。
此外,将这些质量属性转化为具体且可衡量的场景是非常关键的一步。如果没有进行这一步,我们可能就会发现自己陷入了对模糊需求的讨论中,而无法明确地判断这些需求是否得到了真正的满足。
具体来说,一个质量属性场景描述了系统对某种特定交互(或刺激)的可量化响应。这种描述通常涵盖六个基本部分(如图9-3所示),通过这种方式,我们不仅能够清晰地理解需求,还能够为满足这些需求的解决方案提供一个明确的、可操作的框架。这样的方法论不仅提高了设计的准确性,也大大增强了项目团队对质量目标的共识和追求。

在深入讨论系统设计时,理解和定义质量属性场景至关重要。这些场景提供了一种系统化的方法来描述系统在特定交互或刺激下如何量化响应。具体而言,每个质量属性场景由六个细致的组成部分构建,它们共同帮助我们全面而精确地捕捉系统应有的行为和性能。
- 刺激源:指的是触发反应的起点,它可以是人、系统或任何能够产生影响的实体。这一部分识别了需求来源,帮助我们理解是谁或什么需要系统作出响应。
- 刺激:这是指实际的触发事件或动作,它激发系统产生反馈。刺激的性质和类型直接影响系统的响应方式,因此明确刺激的具体形式对于预测和设计系统行为至关重要。
- 环境:环境定义了刺激发生的上下文或条件,包括系统可能面临的特定挑战或限制,如高负载、安全威胁或资源限制等。这一部分强调了外部条件对系统响应的影响,要求设计时考虑到这些变量。
- 构件:这指的是系统内接收刺激并需要作出反应的部分。它可以是系统的一个小部分,如单一模块,或整个系统。通过识别具体的响应构件,我们可以更精确地设计和优化系统结构。
- 响应:响应描述了系统在接收到刺激后的行为或活动。这包括系统采取的具体措施,以及这些措施如何满足需求或解决问题。
- 响应度量:为了确保响应的有效性,我们需要定义可以量化的结果指标。这些度量标准为我们提供了评估系统表现是否符合预期的具体方法。
通过这种详细的框架,我们不仅能够清晰地定义出每个质量属性的具体期望,还能够确保这些期望在系统设计和开发过程中得到有效实现。例如,针对性能和可扩展性的质量属性场景,我们可以使用这个模板来详尽描述系统在这些方面的具体需求和目标(如图10-4所示)。

这些质量属性场景为我们建立了一个实现项目目标的明确路线图,它不仅促进了利益相关者之间的理解和沟通,还为后续的质量保证和测试工作奠定了坚实的基础。通过明确的场景描述,我们能够确保系统设计既符合功能需求,又满足了这些关键的非功能性标准,从而为项目的成功提供了强有力的支持。
1.9设计时需要考虑到隔离
在软件设计的世界里,确保系统各个构建块之间的独立性,是一项极其重要且价值显著的实践。通过把系统的不同部分实现为独立的类库,或者将它们归类到单独的命名空间或包里,我们实际上是在为这些构建块的易管理性铺平了道路。这种做法的优点非常直观:在代码层面,每个模块及其接口的界限变得清晰可见;从团队合作的视角来看,不同的开发人员或团队可以专注于他们负责的模块,而无需过多关注其他模块的实现细节。
尽管如此,除非这些模块被设计为运行在不同的进程中,否则它们之间的独立性实际上是有限的。这背后的原因是什么呢?
设想一个场景,某个开发者对系统中的一个组件进行了修改,这个组件已经被良好地隔离在一个独立的包或类库中,使得在实现阶段,工作能够集中在这个特定的模块上。但是,当开发者试图进行首次功能测试时,她或他仍然不得不确保:
- 系统所有部件的配置设置正确;
- 满足了所有部件的依赖关系,不管是内部库还是外部系统(如数据库系统或消息代理);
- 在系统启动过程中,所有部件的初始化都已正确完成。
在较小的系统中,这些要求可能相对容易满足。然而,在任何中到大型的系统中,维持不同组件的配置和依赖关系往往变成一项极其复杂的任务。
实现系统部件之间完全独立的唯一途径是将它们设计为在不同的进程上运行。目前,微服务架构风格是利用进程分离最著名的实践之一。在微服务架构下,应用被分解为一系列小型、低耦合、可独立部署的服务。
目前,围绕系统应该采用单体架构还是微服务架构的讨论相当热烈。我的观点是,这个问题并不简单,因为除了这两种选择,我们还有其他多种可能性。在设计阶段的任何时刻,我们都应该考虑从单进程单体应用到细粒度服务分解(例如微服务)之间所有的梯度和变体。
将系统的不同部分设计为独立进程,这一做法应被视为一种策略,它带来了既定的优势和一定的复杂性。在定义系统的结构构件时,我们应随时考量是否将它们隔离为独立的进程会带来好处。
当然,与单进程单体架构相比,进程隔离引入了一些新的挑战,包括进程间通信和增加的部署复杂性等。然而,采用进程分离的做法,能够让系统更加易于管理,软件设计更为简洁明了,同时提升了系统部件之间的可替换性,为系统的长期维护和扩展提供了坚实的基础。
假如,我们要开发一个用于编辑矢量图形的桌面应用程序。在这个应用程序的架构设计过程中,很可能会包含一个特殊的组件,我们称之为"光栅化器"。这个光栅化器的核心职责是将由几何形状描述的矢量图形转换成由像素组成的光栅图像,基本上,它把矢量形式的图形文件转化为位图文件。考虑到光栅化器的独特职能和工作方式,将其作为一个独立于图形编辑器主程序之外的进程进行部署是一个很好的选择:
- 光栅化器的主要工作是文件处理,这本质上已经构成了一种进程间通信的自然机制。文件作为一种通用的数据交换格式,可以方便地在不同进程间共享和传递。
- 由于光栅化算法的复杂性,它可能依赖于一些特殊的库,比如专门用于处理位图输入输出的库。这些特定的依赖项可能与我们的编辑器核心功能相去甚远,把它们隔离在一个独立的进程中,可以让核心编辑器保持轻量和专注。
- 光栅化算法不仅复杂而且高度专业化,专为特定的任务定制。通过将光栅化器隔离为一个独立的进程,我们可以减少代码库的混乱,并为这一组件提供更多的实现选择空间。例如,为了获得性能上的优势或更好地利用某些语言特有的库,我们可以选择使用一个与编辑器主体不同的编程语言来实现光栅化器。
在设计阶段的初期,正如我之前在10.1.1节所述,识别系统中的关键抽象组件是非常重要的第一步。一旦我们确定了系统的顶层组件及其大致的内部结构之后,就应当开始考虑潜在的进程分离的可能性,以及这种分离可能带来的各种好处。就像进行任何架构设计决策一样,这种评估自然是设计过程中的一个持续迭代的过程。
同时,值得指出的是,即便在系统的后期,这种分解也是可行的。应用这种分解于一个已经存在的系统,可能会涉及更多的工作量,因为我们通常需要对系统的各个部分进行重构,以使它们能够作为独立的进程来运行。然而,对于一个已经成熟的系统,我们对其有了更深入的理解,这种理解无疑比在设计初期对任何系统的理解都要全面。因此,从这个角度来看,后期进行的分解可能会更加自然,更加符合系统的实际运行经验和需求,使得整个系统架构更加合理和高效。
1.10 总结
在本节的内容回顾中,我们共享了九个关键的系统设计技巧,旨在为你构建软件架构提供实用的指导和启发。这些建议并不是一套严格的设计流程指南,而是旨在在你从初步构思到形成完整软件架构设计的过程中提供帮助。这些建议基于我们多年的实践经验,挑选了那些在实际应用中最为有效的策略。
为了让重点更加突出,我们有意略过了一些更加理论化的讨论,例如"软件架构"的定义细节,以及软件架构与软件设计之间的细微差别等。尽管这些讨论在学术上很重要,且已有众多优秀的文章和研究对其进行了深入探讨,我们选择将焦点放在那些直接影响设计决策和考虑的实际内容上。我们的目标是通过这些直接而实用的建议,帮助你树立明确的质量标准,引导你的软件系统朝着健康和有序的方向发展。
通过这种方式,我们希望能够让你更加容易地理解和采纳这些架构设计的实用技巧,从而使你的项目能够在正确的轨道上平稳推进。