本系列文章通过逐章回答《Fundamentals of Software Architecture》(下文简称 FOSA)一书中的课后思考题,来深入理解书中的核心概念和理论,从而提升我们的软件架构设计能力。本篇为第七章内容。
本章的课后题是:
What is an architectural quantum, and why is it important to architecture?
为什么是架构量子?它为什么对架构很重要?
Assume a system consisting of a single user interface with four independently deployed services, each containing its own separate database. Would this system have a single quantum or four quanta? Why?
假设一个系统包含一个单一的用户界面,以及四个独立部署的服务,每个服务都包含自己的独立数据库。这个系统会有一个量子还是四个量子?为什么?
Assume a system with an administration portion managing static reference data (such as the product catalog, and warehouse information) and a customer-facing portion managing the placement of orders. How many quanta should this system be and why? If you envision multiple quanta, could the admin quantum and customer-facing quantum share a database? If so, in which quantum would the database need to reside?
假设一个系统包含两个部分:
- 管理后台:负责管理静态参考数据(例如产品目录、仓库信息)。
- 用户端:负责处理客户订单的下达。
这个系统应该被划分为多少个 量子?为什么?如果您设想这是多个量子,那二者可以共享同一个数据库吗?如果可以,该数据库需要驻留在哪个量子中?
什么是架构量子?
架构量子 (Architectural Quantum) 这个概念源于 Neal Ford、Mark Richards 等人在《软件架构:艰难的部分》(Software Architecture: The Hard Parts) 一书中提出的。它的核心定义是:
一个架构量子是指一个系统中,具有高功能内聚性 (High Functional Cohesion) 和同步部署依赖性 (Synchronous Deployable Dependency) 的、可独立部署的组件的最小集合。
为了更好地理解这个定义,我们需要拆解其中的关键术语:
- 可独立部署的组件 (Independently Deployable Component):这是现代架构(尤其是微服务架构)的基本单元。它可以是一个服务、一个应用或者任何可以独立于系统其他部分进行部署的模块。
- 高功能内聚性 (High Functional Cohesion):这个概念借鉴了软件工程中的“内聚性”,指的是一个组件内部的各个部分为了一个共同、明确的目标而紧密协作。例如,一个“订单处理服务”应该只包含与创建、更新、查询订单相关的逻辑,而不应该包含用户认证或产品推荐的逻辑。一个架构量子内的所有组件,共同构成了一个完整且内聚的业务功能。
- 同步部署依赖性 (Synchronous Deployable Dependency):这是定义中最关键也最“硬核”的部分。它指的是组件之间的行为调用必须是同步的,以保证系统正常工作。如果服务 A 调用服务 B,并且必须等待 B 的响应才能继续执行,那么 A 和 B 之间就存在同步依赖。这种依赖关系会将不同的独立部署组件“捆绑”在一起,形成一个不可分割的整体,也就是一个量子。如果为了让某个功能正常工作,你必须同时部署或更新服务 A 和服务 B,那它们就属于同一个量子。
为什么架构量子很重要?
理解了定义后,我们来看看它在实践中的重要性。架构量子的概念为我们提供了一个强大的分析工具,帮助我们衡量和决策架构中的关键架构特性,例如:
- 可部署性 (Deployability):一个架构量子是最小的独立部署单元。整个量子可以作为一个单元进行部署、回滚和发布,而不会破坏系统的其他部分。这极大地简化了 CI/CD 流程。如果你错误地将一个量子拆分成多个,可能会导致部署时的级联失败。
- 可测试性 (Testability):由于量子内部的组件功能高度内聚且存在同步依赖,因此它也成为了一个理想的测试边界。你可以对整个量子进行端到端的功能测试和集成测试,而无需启动整个庞大的系统。
- 可伸缩性 (Scalability):不同的量子承载不同的业务功能,其负载模式也可能完全不同。例如,浏览产品目录的量子和处理支付的量子对资源的需求天差地别。将它们划分为不同的量子,使得我们可以独立地扩展每一个量子,从而更高效地利用资源。
- 容错性 (Fault Tolerance):一个设计良好的量子边界可以形成一道“防火墙”。一个量子的失败(例如,由于代码缺陷或流量激增)不应该导致其他量子的同步崩溃。这种隔离性是构建高可用系统的基础。
- 组织结构对齐 (Alignment with Team Structure):根据康威定律 (Conway's Law),系统架构往往会反映出开发它的团队的沟通结构。一个清晰的量子可以由一个独立的、自治的团队负责,从而减少跨团队沟通的开销,提升开发效率。
简而言之,架构量子帮助我们识别出系统中真正的、不可再分的架构单元。它提供了一个明确的边界,指导我们如何合理地拆分系统,从而在可部署性、可伸缩性、容错性和团队效率之间取得平衡。
场景分析一:单一 UI + 四个独立服务
假设一个系统包含一个单一的用户界面,以及四个独立部署的服务,每个服务都包含自己的独立数据库。这个系统会有一个量子还是四个量子?为什么?
答案是:这个系统最有可能包含四个量子 (Four Quanta)。
分析如下:
这里的关键信息是“四个独立部署的服务,每个服务都包含自己的独立数据库”。
- 独立部署与自有数据库:这个设定强烈暗示了服务之间的高度解耦。在现代架构中,服务独占自己的数据库是实现真正自治和独立部署的黄金法则。如果服务间共享数据库,它们的部署就会产生耦合(例如,一个服务修改了表结构,可能会影响到所有依赖该表的其他服务),也就无法做到真正的独立部署。
- 同步依赖的缺失:虽然这四个服务最终都服务于同一个用户界面 (UI),但题目并未描述它们之间存在同步调用的强依赖关系。UI 很可能是通过异步的方式或者直接独立地与这四个服务进行通信。例如,UI 的一个页面可能需要同时展示来自服务 A 的用户信息和服务 B 的产品列表,但 UI 可以分别向 A 和 B 发起两个独立的 API 请求,这两个服务之间并不需要直接对话。
- 功能内聚性:每个服务和它自己的数据库共同构成了一个高度内聚的功能单元。例如,服务 A 和它的数据库负责“用户管理”,服务 B 和它的数据库负责“订单管理”,等等。它们各自完成了闭环的业务能力。
结论:由于这四个服务(连同其数据库)可以独立部署,并且它们之间大概率不存在必须同步成功的强依赖,因此它们构成了四个独立的架构量子。单一的用户界面在这里扮演的是一个“集成层”或“客户端”的角色,它本身通常不被视为一个量子,而是作为这些量子的消费者。将系统划分为四个量子,使得每个服务都可以被独立地开发、测试、部署和扩展,从而获得了极大的架构灵活性。
场景分析二:管理后台 + 用户端
假设一个系统包含两个部分:
- 管理后台:负责管理静态参考数据(例如产品目录、仓库信息)。
- 用户端:负责处理客户订单的下达。
这个系统应该被划分为多少个量子?为什么?如果您设想这是多个量子,那二者可以共享同一个数据库吗?如果可以,该数据库需要驻留在哪个量子中?
这个系统应该被划分为多少个量子?为什么?
答案是:这个系统应该被划分为两个量子 (Two Quanta)。
分析如下:
- 不同的架构特性需求:
- 用户端 (Customer-facing Portion):这是系统的核心交易部分。它需要高可用性 (High Availability)、高可伸缩性 (High Scalability)(因为用户流量波动大,尤其在促销期间)、以及低延迟 (Low Latency)。
- 管理后台 (Administration Portion):这部分主要由内部员工使用。它对可伸缩性的要求远低于用户端,但可能对数据一致性 (Consistency) 和安全性有更高的要求。其使用模式也更可预测。
- 功能内聚性与关注点分离:管理后台的功能(管理产品目录、仓库信息)和用户端的功能(浏览商品、下单、支付)在业务上是完全不同的。将它们分开,符合单一职责原则,也使得各自的逻辑更清晰。
- 部署和生命周期的独立性:用户端的功能可能需要频繁迭代和快速发布(例如,上线一个新的促销活动),而管理后台的功能则相对稳定,更新频率较低。将它们划分为两个量子,可以实现独立的部署和发布节奏,用户端的紧急修复或更新不会被后台的发布流程所拖累。
结论:基于截然不同的架构特性需求、功能内聚性以及部署独立性的考量,将这个系统划分为一个“管理后台量子”和一个“用户端量子”是最佳实践。
二者可以共享同一个数据库吗?
答案是:技术上可以,但强烈不推荐 (Technically possible, but highly discouraged)。共享数据库会引入我们之前提到的问题,即耦合 (Coupling)。
- 性能耦合:管理后台的一个慢查询或数据批量导入操作,可能会锁住表,从而严重影响用户端的性能,甚至导致用户无法下单。
- 部署耦合:如果用户端需要修改某个表的结构来支持新功能,这个修改可能会破坏管理后台的正常工作,反之亦然。这使得两个本应独立的量子在部署上产生了依赖。
- 安全耦合:用户端和管理后台的数据库访问权限需求是不同的。共享数据库会增加权限管理的复杂性,可能导致安全漏洞。
如果一定要共享,数据库需要驻留在哪个量子中?
这是一个权衡和妥协的问题。如果因为历史原因、成本限制或其他因素不得不共享数据库,那么决策的关键在于数据的所有权 (Data Ownership) 和服务的关键性 (Service Criticality)。
在这个场景中,“产品目录”和“仓库信息”这些数据,虽然由管理后台进行维护,但它们的最终消费者和价值实现者是用户端。用户下单的逻辑严重依赖于这些数据的可用性和准确性。
因此,如果必须共享,该数据库在逻辑上应该驻留在用户端量子中。
原因如下:
- 业务关键性:用户端是直接产生商业价值的部分,其可用性是第一位的。将数据库置于此量子内,意味着所有架构决策(如扩展、备份、容灾)都将优先保障用户端的需求。
- 数据所有权:虽然管理后台是数据的“生产者”,但用户端是数据的核心“消费者”。在领域驱动设计 (Domain-Driven Design) 的思想中,数据应该属于它所支持的核心业务领域 (Core Domain),在这里显然是用户交易领域。
- 架构上的清晰性:这样做可以建立一个清晰的依赖关系:管理后台量子依赖于用户端量子中的数据。这虽然不是最理想的解耦状态,但至少依赖关系是单向且明确的。
在这种共享模式下,更好的实践是通过定义稳定的 API 来缓解耦合。管理后台不应直接操作数据库,而是应该通过用户端量子提供的 API 来修改产品目录等数据。这样做可以隐藏数据库的物理实现,为未来的数据库拆分创造可能性。