现代架构模式

共 21 题
📑 题目列表 21 题
#
★★★

1. 管道-过滤器架构中过滤器如何通过管道组合,数据转换的粒度与并行性,与责任链/事件流的差异?

请解释管道-过滤器架构,说明过滤器如何通过管道组合,数据转换的粒度与并行性,以及它与责任链/事件流的差异?

  • 过滤器(filter)与管道(pipe)的组合
  • 数据转换粒度与并行性
  • 与责任链/事件流的差异
  • 管道-过滤器架构:系统由多个过滤器(filter)组成,过滤器之间通过管道(pipe)连接,数据从源经过管道流经每个过滤器,每个过滤器做一次数据转换(处理 + 输出),串联成一条处理链。每个过滤器是独立的处理单元,输入输出标准化。
  • 数据转换粒度与并行性:过滤器粒度决定转换规模(可粗可细),粒度细则复用性好但要更多管道;并行性指多个过滤器可并行处理不同数据(流水线,如 filt1 处理数据 A 时 filt2 处理数据 B),或过滤器内部并行。并行性受"数据依赖"限制,无依赖的过滤器可并行。
  • 与责任链/事件流的差异:
    • 责任链:每个处理器决定是否处理并/或传给下一个,链是"可中断"的,强调"谁能处理"的决策;管道-过滤器是"每个过滤器都处理所有数据",强调"顺序变换"。
    • 事件流:事件流是异步、解耦、按事件分发(发布/订阅),过滤器是无状态、同步、明确的数据流管道;事件流更松散,管道-过滤器更结构化。

管道-过滤器是"数据流式顺序变换",每个过滤器都处理全部数据;责任链是"选择性处理 + 可中断";事件流是"异步解耦 + 事件驱动"。适用场景不同:管道过滤器适合明确的数据处理流水线(如编译器、图像处理)。

#
★★★

2. 微服务 vs 单体 vs 模块化单体中业务复杂度、团队结构与部署频率如何影响架构选择?

请对比微服务、单体与模块化单体,说明业务复杂度、团队结构与部署频率如何影响架构选择?

  • 三种架构的形态与演进
  • 业务复杂度、团队规模、部署频率的影响
  • 选择的权衡
  • 单体(Monolith):所有功能在一个应用/部署单元内,模块间通过方法调用。简单、部署快、无网络开销,但随规模增长难扩展、难维护、整体部署。
  • 模块化单体(Modular Monolith):仍是单一体部署,但内部按模块/边界清晰划分(模块间通过接口),把"模块边界的清晰"与"单体的简单"结合,是走向微服务前的过渡。
  • 微服务(Microservices):把功能拆成独立部署的服务,各自独立版本、独立数据库、独立团队。职责清晰、独立扩展与部署,但分布式复杂度高(网络、事务、一致性、运维)。

选择的影响因素:

  • 业务复杂度:业务简单用单体;业务复杂、模块边界清晰且需要独立扩展/独立团队时用微服务。
  • 团队结构:团队小(几个团队)用单体/模块化单体,避免微服务的运维负担;团队大(多团队并行)且能按服务划分归属时,微服务能减少团队间协调。
  • 部署频率:需要高频独立部署、不同模块发布节奏不同时,微服务更合适;发布频率低、统一发布时单体更简单。

结论:没有绝对最佳,按 Conway 定律,架构应匹配团队结构与协作方式。常见路径:单体 → 模块化单体(先划清边界)→ 必要后再拆微服务。

选择是"模块独立性 vs 分布式复杂度"的权衡。微服务适合"业务复杂 + 大团队 + 高独立部署频率",否则用单体/模块化单体更经济。模块化单体是低风险的第一步。

#
★★★

3. 架构评估中 ATAM 如何用质量属性场景(刺激-响应-度量)量化权衡,与架构评审的区别

请解释架构评估方法 ATAM,说明它如何用质量属性场景(刺激-响应-度量)量化权衡,以及与架构评审的区别?

  • ATAM 的步骤与质量属性场景
  • 刺激-响应-度量(stimulus-response-measure)
  • 与架构评审的区别
  • ATAM(Architecture Tradeoff Analysis Method):一种系统的架构评估方法,通过分析架构对质量属性(性能、可用性、可修改性、安全性等)的满足程度,识别架构决策的权衡与风险。它把架构与业务目标、质量属性对齐。
  • 质量属性场景:ATAM 用场景(scenario)描述对质量属性的具体需求,场景由刺激(stimulus)(什么事件触发)、环境(environment)(在什么条件下)、响应(response)(系统如何响应)、响应度量(measure)(可量化的指标,如 P99 延迟、MTTR)组成。通过场景明确"系统应如何表现"并量化,从而评估架构是否满足。
  • 权衡分析:ATAM 识别架构决策(tactics)分别影响哪些质量属性,找出"权衡点"(tradeoff,如追求性能牺牲可维护性)与"风险点",形成架构评估报告。
  • 与架构评审的区别:架构评审通常是主观的、基于经验的专家评审,关注"架构是否合理、是否符合最佳实践";ATAM 是结构化、可重复、基于场景与度量的方法,更客观地量化评估架构对质量属性的满足程度,并系统识别权衡与风险。评审偏形式/经验,ATAM 偏场景/度量/权衡。

ATAM 用"刺激-响应-度量"把抽象的质量属性变成可量化的场景,再分析架构决策带来的权衡与风险,比主观评审更客观、可重复。

#
★★★

4. 微前端中应用拆分与集成方式(模块联邦/iframe),样式与状态隔离的边界及共享依赖策略

请解释微前端,说明应用的拆分与集成方式(模块联邦/iframe),样式与状态隔离的边界,以及共享依赖策略?

  • 微前端的应用拆分与独立部署
  • 集成方式(模块联邦/iframe)
  • 样式与状态隔离、共享依赖
  • 微前端:把单体前端应用拆分成多个独立开发、独立部署的子应用(micro-frontend),由主应用(host)负责集成与路由。每个子应用可独立团队维护、独立发版,对应后端微服务。
  • 集成方式:
    • iframe:每个子应用跑在 iframe 中,天然隔离(样式、状态、安全沙箱),但通信跨域麻烦、性能差、体验割裂。
    • 模块联邦(Webpack Module Federation):运行时共享模块,主应用动态加载子应用的入口模块,子应用间可共享依赖与组件,通信方便、性能好,是主流方案。
    • 其他:single-spa(路由分发)、qiankun(基于 single-spa + 沙箱)。
  • 样式与状态隔离:样式隔离用 CSS 命名空间/Shadow DOM/模块联邦的 scoped 样式,避免全局样式冲突;状态隔离用各自的全局状态(Redux 需 namespace/沙箱),或通过自定义事件/全局事件总线通信。
  • 共享依赖策略:把公共依赖(React、工具库)提升到主应用统一加载(externals/singleton),子应用复用,避免重复打包与版本冲突;但共享库要选稳定版本,避免升级牵动所有子应用。

微前端核心是"拆分 + 独立部署 + 运行时集成"。iframe 隔离强但体验差,模块联邦集成好但需处理依赖与沙箱;样式/状态隔离与共享依赖是设计重点。

#
★★

5. Serverless 的约束中函数粒度与冷启动延迟,状态外置(数据库/对象存储)与无状态设计如何影响架构?

请解释 Serverless 的约束,说明函数粒度与冷启动延迟,以及状态外置与无状态设计如何影响架构?

  • 函数粒度与冷启动延迟
  • 无状态设计要求
  • 状态外置(数据库/对象存储)的影响
  • Serverless:函数即服务(FaaS),开发者只写函数,平台负责运行、伸缩、计费。函数按需调用、按使用量计费,自动弹性伸缩到零。
  • 函数粒度与冷启动延迟:函数粒度应足够小(单职责),但也别过细(过多函数调用增加开销与延迟);冷启动(cold start)指首次调用需初始化运行时(容器/VM 启动、加载依赖),延迟可达数百 ms 到秒级,影响响应时间。缓解:预留并发(provisioned concurrency)、减少依赖、保持函数轻量、热实例复用。
  • 无状态设计:函数通常是"无状态"的(每次调用可能是新实例),因此不能依赖进程内状态,状态必须外置到数据库/对象存储/缓存(Redis/S3)。这要求业务逻辑写成"无状态 + 状态外置",提升了可伸缩性但增加了跨网络访问延迟与一致性考虑。
  • 影响:状态外置让函数无状态、可无限伸缩,但每次访问外部存储有网络开销;冷启动与状态外置是 Serverless 的主要架构约束,适合突发、短时、事件驱动工作负载,不适合长连接、重型计算、强状态场景。

Serverless 的约束核心是"无状态 + 冷启动"。函数粒度要小、状态要外置,冷启动延迟需用预留并发缓解;架构要设计成事件驱动、无状态、可伸缩。

#
★★

6. Space-Based Architecture 中如何用内存数据网格(Hazelcast/GigaSpaces)实现弹性伸缩,与缓存+数据库架构的差异?

请解释 Space-Based Architecture(空间架构),说明如何用内存数据网格(Hazelcast/GigaSpaces)实现弹性伸缩,以及它与缓存+数据库架构的差异?

  • 内存数据网格(IMDG)与空间分区
  • 弹性伸缩(无中心数据库瓶颈)
  • 与缓存+数据库架构的差异
  • Space-Based Architecture:把内存中的数据网格(In-Memory Data Grid, IMDG)作为核心存储,业务数据分布式地分片存于各节点内存中,节点间通过网格同步。典型实现:Hazelcast、GigaSpaces。
  • 弹性伸缩:数据按 key 分片(partition)分布到多个节点,每个节点既是计算单元又是数据持有者,无中心数据库瓶颈。新增节点时自动迁移分片(rebalance),实现线性伸缩;避免"所有请求都打到单一数据库"的瓶颈。读写都直接访问内存网格,延迟低。
  • 与缓存+数据库架构的差异:缓存+数据库架构中,数据库是持久化真相,缓存只是加速层(缓存未命中回源数据库),仍有数据库瓶颈;Space 架构中,内存网格本身就是主要数据存储(通常配合异步持久化/副本),数据分布在节点上,无单一数据库热点,伸缩性更好。但 Space 架构需要处理数据持久化、分片一致性、故障时副本重建,复杂度更高。

Space 架构把"数据"与"计算"一起分布在节点上,消除数据库单点,实现水平弹性伸缩;与"缓存+数据库"相比,它把内存作为主存储而非加速层,伸缩性更强但持久化与一致性更复杂。

#
★★

7. 整洁架构/六边形架构中依赖倒置如何让业务核心不依赖框架与数据库?

请解释整洁架构(Clean Architecture)与六边形架构,说明依赖倒置如何让业务核心不依赖框架与数据库?

  • 依赖倒置原则(DIP)
  • 业务核心与框架/数据库解耦
  • Clean/Hexagonal 的边界
  • 核心思想:业务核心(领域/用例)不依赖框架、数据库、UI 等外部细节,而是通过"依赖倒置"让这些外部细节依赖核心定义的接口。
  • 依赖倒置(DIP):高层模块(业务核心)不依赖低层模块(框架/数据库),两者都依赖抽象(接口)。核心定义端口(interface,如 Repository 接口),外部适配器(数据库实现、框架适配器)实现这些接口,并通过依赖注入(DI)把具体实现注入核心。这样核心只依赖接口,不依赖具体实现,外部细节(框架、数据库)可替换。
  • 整洁架构的分层:实体(entities)→ 用例(use cases)→ 接口适配器(interface adapters)→ 框架(frameworks)。依赖方向由外向内(箭头指向内层),内层不依赖外层,外层依赖内层接口。
  • 六边形架构:核心领域在中心,通过"端口与适配器"连接外部世界;驱动适配器(inbound,如 HTTP 控制器)与被驱动适配器(outbound,如数据库)都依赖核心定义的端口。

依赖倒置是让"核心定义接口、外部实现接口、DI 注入"三步,使业务核心只依赖抽象,框架/数据库成为可替换的外层,从而提升可测试性与可维护性。

#
★★

8. 模块化单体与微服务中什么阶段拆分、团队规模与数据边界如何判断?

请解释模块化单体与微服务的选择,说明什么阶段拆分、团队规模与数据边界如何判断?

  • 模块化单体作为过渡
  • 拆分时机与团队规模
  • 数据边界判断
  • 模块化单体:单单一部署单元,但内部按模块边界清晰划分(模块间通过接口、数据隔离),具备"清晰边界"与"单体简单"的优点,是微服务前的过渡。
  • 什么阶段拆分:先做模块化单体(划清模块边界、数据隔离),当模块边界清晰、且出现"独立部署/独立扩展/独立团队"的强需求时,才拆成微服务。避免过早拆分带来的分布式复杂度。
  • 团队规模判断:团队小(1-2 个团队)时单体/模块化单体更经济,避免多服务运维负担;团队大(多个团队并行、可按服务划分归属)时,微服务能减少团队间协调,符合 Conway 定律。
  • 数据边界判断:模块/服务边界应与数据边界一致——每个服务拥有自己的数据库(或数据表),不共享库;按"业务能力/子域"划分数据边界,跨服务数据通过 ID 引用或事件同步,而非直接访问。数据边界清晰(能独立演化)才适合拆成微服务。

判断是否拆分:先模块化单体划清边界,再按"独立部署需求 + 团队规模 + 数据边界清晰度"决定是否拆微服务。数据边界是拆分的关键,共享库是拆分障碍。

#
★★

9. 整洁架构的依赖规则中依赖只能向内,框架与数据库如何成为可替换的外层?

请解释整洁架构的依赖规则,说明依赖为什么只能向内,以及框架与数据库如何成为可替换的外层?

  • 依赖方向规则(依赖只能向内)
  • 内层定义接口、外层实现
  • 框架/数据库可替换
  • 依赖规则:整洁架构中,依赖箭头只能指向内层(从框架/适配器指向用例/实体),内层永远不依赖外层。内层是业务核心,定义接口与业务规则;外层是框架、数据库、UI 等实现细节。
  • 为什么只能向内:依赖方向决定"谁依赖谁"。如果内层依赖外层(如实体依赖 JPA、用例依赖具体数据库),则业务核心被框架/数据库绑定,变更框架就要改核心,且核心难以测试。依赖向内保证核心是稳定的、可独立测试的。
  • 框架/数据库可替换:内层定义端口(接口,如 Repository 接口、用例接口),外层实现这些接口(JPA 实现、具体数据库驱动),通过依赖注入在运行时注入。这样换数据库只改外层适配器,不动核心;换框架(如从 Spring 换掉)只改外层适配层。核心与框架/数据库通过接口解耦,因此外部细节可替换。

依赖向内 + 接口定义 + DI 注入,实现"核心稳定、外部可替换"。核心只依赖抽象,框架/数据库是插在适配层的实现,可插拔、可测试。

#
★★

10. 模块化单体 vs 微服务中团队规模、部署频率与数据边界如何权衡?

请对比模块化单体与微服务,说明团队规模、部署频率与数据边界如何权衡?

  • 团队规模的影响
  • 部署频率的影响
  • 数据边界的权衡
  • 团队规模:小团队(<2 个团队)用模块化单体,降低运维与通信成本;大团队(多团队)且按服务划分归属时,微服务可减少团队间协调,各自独立演进。团队规模小却用微服务,会因运维负担过重而拖慢。
  • 部署频率:若不同模块发布节奏差异大、需要独立高频部署,微服务更合适(独立部署单元);若统一发布、发布频率低,模块化单体更简单(一次部署全部)。微服务的高频独立部署是收益,但需要 CI/CD、监控、服务治理支撑。
  • 数据边界:模块化单体通常共享数据库但逻辑隔离;微服务要求每个服务独立数据库(数据边界清晰),跨服务通过 API/事件交互。数据边界能清晰划分(可独立演化)才适合微服务;若数据强耦合、难以分库,则模块化单体更现实。

权衡结论:模块化单体适合"小团队 + 低发布频率 + 数据耦合";微服务适合"大团队 + 高独立部署频率 + 数据边界清晰"。根据三者权衡,模块化单体是低风险起点。

权衡三角是"团队规模、部署频率、数据边界"。三者都指向"独立性强"时选微服务,否则模块化单体更经济。数据边界是硬约束,共享库阻碍微服务拆分。

#
★★

11. 平台工程中内部开发者平台如何通过自助服务与黄金路径提升交付效率,与 DevOps 的关系

请解释平台工程(Platform Engineering),说明内部开发者平台(IDP)如何通过自助服务与黄金路径提升交付效率,以及它与 DevOps 的关系?

  • 内部开发者平台(IDP)与自助服务
  • 黄金路径(golden path)
  • 与 DevOps 的关系
  • 平台工程:通过构建内部开发者平台(IDP),把基础设施、服务部署、CI/CD、可观测性等能力封装成自助服务,让开发者自助地获取资源、部署服务,减少重复劳动与等待。
  • 自助服务:IDP 提供自助门户/API(如 Backstage),开发者通过模板/命令自助创建服务、申请环境、配置 CI/CD、部署上线,无需等待运维手工处理,提升交付速度。
  • 黄金路径(golden path):平台定义"推荐的目标路径"(preferred 支架/模板),开发者按黄金路径开发即可获得最佳实践(安全、可观测性、可部署性开箱即用),减少决策成本与错误配置。黄金路径让"正确的事"成为"最简单的事"。
  • 与 DevOps 的关系:DevOps 强调开发与运维协作的文化与实践;平台工程把 DevOps 实践沉淀为"平台/工具",通过自助服务让开发者更容易践行 DevOps(不需要每个团队自己搭全套工具链)。平台工程是 DevOps 的工程化/产品化,提供"自服务的 DevOps 能力"。

IDP 的核心是"自助服务 + 黄金路径",把最佳实践固化到平台,降低开发者环境/部署成本;它是 DevOps 文化落地为平台产品的工程化手段,二者互补而非替代。

#

12. Clean Architecture 的分层中实体、用例(use case)、接口适配器、框架四层的职责与依赖规则,测试如何受益?

请解释 Clean Architecture 的四层(实体、用例、接口适配器、框架),说明各层职责与依赖规则,以及测试如何受益?

  • 四层职责与依赖方向
  • 依赖倒置与接口
  • 测试收益(可 mock、可独立测试)
  • 四层(由内到外):
    1. 实体(Entities):业务对象与核心业务规则,不依赖任何外部。
    2. 用例(Use Cases):应用特定业务逻辑,编排实体与端口,定义用例接口。
    3. 接口适配器(Interface Adapters):把外部输入(HTTP 控制器、消息监听)转换为用例调用,并持有 Repository 实现;把核心数据转换为外部格式。
    4. 框架(Frameworks):数据库、Web 框架、UI 等具体实现细节。
  • 依赖规则:依赖只能向内(外层依赖内层,内层不依赖外层)。内层定义接口(端口),外层实现接口并通过依赖注入注入。例如用例定义 Repository 接口,框架层用 JPA 实现。
  • 测试受益:因为核心(实体/用例)只依赖接口,不依赖框架/数据库,测试时可用 mock 或内存实现替换数据库/框架,快速单测核心业务逻辑;无需真实数据库、无需启动 Web 容器,测试快且稳定。核心与外部隔离让单元测试聚焦业务,集成测试只需验证适配层。

四层职责分明 + 依赖向内 + 接口注入,让核心可独立测试(mock 外部),测试成本低、稳定性高,是 Clean Architecture 的核心收益。

// 核心定义接口(端口)
interface OrderRepository { Order findById(Long id); }
// 用例只依赖接口
class CreateOrderUseCase {
    private final OrderRepository repo;
    CreateOrderUseCase(OrderRepository repo) { this.repo = repo; }
    void execute(Order o) { repo.save(o); }
}
// 测试用 mock 实现
class CreateOrderUseCaseTest {
    @Test void works() {
        OrderRepository mock = mock(OrderRepository.class);
        new CreateOrderUseCase(mock).execute(new Order());
    }
}
#

13. 六边形(端口-适配器)架构中端口如何定义核心与外部世界的契约,驱动/被驱动适配器的分类与依赖注入的配合?

请解释六边形(端口-适配器)架构,说明端口如何定义核心与外部世界的契约,驱动/被驱动适配器的分类,以及与依赖注入的配合?

  • 端口(端口)定义核心与外部契约
  • 驱动(inbound)与被驱动(outbound)适配器
  • 依赖注入配合
  • 六边形架构:核心领域在中心(六边形),外围通过"端口与适配器"与外部世界交互。端口是核心定义的接口,定义核心与外部世界的契约(做什么),适配器是端口的具体实现(怎么做)。
  • 端口分类:驱动(inbound/primary)端口是核心暴露给外部(如用例接口),被驱动(outbound/secondary)端口是核心需要外部提供的能力(如 Repository 接口、消息发送接口)。
  • 适配器分类:驱动适配器(inbound,如 HTTP 控制器、命令行)调用核心的驱动端口;被驱动适配器(outbound,如数据库实现、消息队列实现)实现核心的被驱动端口。核心不直接依赖任何适配器。
  • 依赖注入配合:运行时通过依赖注入(DI 容器)把适配器实现注入到核心的端口引用中,如把 JPA 的 OrderRepository 实现注入用例。DI 让核心只认接口,实现可替换(换 DB、换 MQ 只改适配器)。

六边形把"端口(契约)+ 适配器(实现)+ DI(注入)"结合,核心隔离外部通过接口,适配器按方向分驱动/被驱动,实现依赖反转与可替换。

#

14. 整洁架构的依赖规则中领域层不能依赖基础设施,端口(interface)如何让数据库与框架可替换,测试如何受益?

请解释整洁架构的依赖规则,说明为什么领域层不能依赖基础设施,端口如何让数据库与框架可替换,以及测试如何受益?

  • 领域层与基础设施解耦
  • 端口(interface)实现可替换
  • 测试收益
  • 为什么领域层不能依赖基础设施:领域层是业务核心,若依赖基础设施(数据库、框架、消息等),则业务规则被外部细节绑定,改数据库/框架就要改领域,且领域难以独立测试。领域层应只表达业务,不关心"用什么库、怎么存"。
  • 端口(interface)让数据库与框架可替换:领域层定义端口(如 Repository 接口),基础设施层实现该接口(JPA 实现、具体 DB 驱动)。领域只依赖接口,不依赖具体实现;运行时用 DI 注入。这样数据库/框架成为领域中接口的"可替换实现",换数据库只改基础设施实现,领域不动。
  • 测试受益:因为领域只依赖端口(接口),测试时用 mock 实现或内存实现替换真实数据库/框架,可快速单测领域逻辑,无需真实 DB、不启动容器,测试快、稳定、隔离。这使领域层的核心逻辑可被充分验证。

领域层依赖端口而非基础设施,把"业务"与"如何实现"解耦;DI 注入可替换实现,测试用 mock 替换,从而提升领域可测试性与可维护性。

#

15. 微内核架构的插件机制中核心系统与插件如何通过契约解耦,插件的注册、隔离与版本管理(如 OSGi/Eclipse)?

请解释微内核架构的插件机制,说明核心系统与插件如何通过契约解耦,以及插件的注册、隔离与版本管理(如 OSGi/Eclipse)?

  • 核心系统与插件通过契约解耦
  • 插件注册、隔离与版本管理
  • OSGi/Eclipse 的插件机制
  • 微内核架构:核心系统(kernel)提供最小核心功能与扩展点(契约/接口),插件通过扩展点动态加载,为核心添加功能。核心与插件通过**契约(接口/扩展点)**解耦——核心定义接口,插件实现接口,核心不依赖具体插件。
  • 插件注册:插件通过清单/注册表(manifest、plugin.xml)声明自己提供的扩展,核心在启动时扫描/加载符合条件的插件,或在运行时按需加载。加载机制如 ServiceLoader、反射、动态类加载。
  • 插件隔离:插件运行在隔离的类加载器/模块中,避免插件间类冲突,插件崩溃不影响核心与其他插件。OSGi 用 Bundle + 独立 ClassLoader 实现隔离与依赖解析。
  • 版本管理:插件有独立版本,核心管理插件依赖关系与版本兼容;支持插件热插拔/独立升级。OSGi 的 Bundle 提供版本化依赖解析(Import-Package/Export-Package),Eclipse 基于 OSGi 的插件体系。

微内核 = 核心 + 契约(扩展点)+ 插件。契约解耦、注册扫描、隔离加载、版本管理四要素,OSGi/Eclipse 是成熟实现。

#

16. SOA 与微服务的演进中 ESB 集中治理 vs 去中心化通信,服务契约(WSDL/OpenAPI)与版本兼容如何管理?

请解释 SOA 与微服务的演进,说明 ESB 集中治理与去中心化通信的差异,以及服务契约(WSDL/OpenAPI)与版本兼容如何管理?

  • SOA 的 ESB 集中治理 vs 微服务去中心化
  • 服务契约(WSDL/OpenAPI)
  • 版本兼容管理
  • SOA:面向服务架构,常通过 ESB(企业服务总线)集中管理服务路由、协议转换、编排。SOA 偏重服务复用与集中治理,服务较粗粒度,通信常用 SOAP/WS。
  • 微服务:去中心化,服务间直接通信(HTTP/REST、gRPC、消息),无中心 ESB,每个服务独立治理、独立部署。微服务更细粒度、更独立,治理下沉到服务(自包含的契约、配置、监控)。
  • 演进:SOA → 微服务是"从集中治理到去中心化",从重 ESB 到轻量去中心化通信,从粗粒度服务到细粒度自治服务,从共享数据库到独立数据库。
  • 服务契约(WSDL/OpenAPI):契约定义服务的能力与消息格式。SOA 用 WSDL(XML 描述),微服务用 OpenAPI(REST 描述)或 protobuf(gRPC)。契约是服务间耦合的界面。
  • 版本兼容管理:契约演进要兼容。策略:新增字段给默认值(向后兼容)、删除字段先废弃(deprecated)后移除、用语义化版本(SemVer)标识破坏性变更;服务提供多版本(v1/v2)并存,或网关做版本路由;破坏性变更需协调消费者。WSDL 用 namespace 版本,OpenAPI 用路径版本。

SOA 用 ESB 集中治理,微服务去中心化直接通信;契约(WSDL/OpenAPI)定义服务界面,版本兼容靠"新增默认值、废弃再移除、语义化版本 + 多版本并存"管理。

#

17. 事件溯源与 CQRS 的组合中写模型与读模型的分离如何解决扩展性,复杂性代价在哪?

请解释事件溯源(Event Sourcing)与 CQRS 的组合,说明写模型与读模型分离如何解决扩展性问题,以及复杂性代价在哪里?

  • 写模型(聚合)与读模型(投影)分离
  • 独立扩展读侧
  • 复杂性代价
  • 组合:写侧用事件溯源(聚合状态由事件流重放),命令产生领域事件;读侧用 CQRS 的独立读模型(投影表),由投影器从事件构建。两者通过事件流连接。
  • 扩展性收益:读模型与写模型完全独立,可分别扩展——读侧可加多个投影、多副本、为不同查询建专用读模型(宽表、物化视图),读吞吐可独立水平扩展;写侧专注聚合与一致性,不被读查询拖累。事件流可重放重建任意读模型,支持审计与时间旅行。
  • 复杂性代价:事件溯源 + CQRS 引入大量复杂度——需要事件存储与版本管理、投影器(维护多个投影、处理乱序/重复/失败事件)、写读最终一致的延迟窗口、幂等与去重、事件 Schema 演进。开发与运维成本高,逻辑分散,调试困难。适用于高写入并发、需要审计、读多写少且查询多样化的场景,普通业务用传统 CRUD 更简单。

读写分离 + 事件溯源带来"读侧独立扩展 + 可重放 + 审计",代价是最终一致、事件存储、投影维护与 Schema 演进等复杂度。收益显著但复杂,需按业务权衡。

#

18. 六边形/端口-适配器架构中核心领域如何通过端口隔离基础设施依赖?

请解释六边形(端口-适配器)架构,说明核心领域如何通过端口隔离基础设施依赖?

  • 端口(接口)定义核心对外契约
  • 基础设施作为适配器实现
  • 依赖倒置与隔离
  • 六边形架构:核心领域在中心,通过端口(port,接口)与外部世界交互。端口是核心定义的能力契约,适配器(adapter)是实现这些契约的基础设施代码。
  • 隔离方式:核心领域只依赖端口(接口),不依赖任何具体基础设施(数据库、消息、HTTP)。例如核心定义 Repository 端口,数据库实现(JPA)作为被驱动适配器实现该端口;核心定义用例端口,HTTP 控制器作为驱动适配器调用它。
  • 依赖倒置:依赖方向从"核心依赖基础设施"反转成"基础设施依赖核心定义的端口"。基础设施通过依赖注入被注入到核心,核心可见的只是接口,因此换数据库/换框架对核心透明。
  • 收益:核心不受基础设施变更影响、可独立测试(mock 端口实现)、基础设施可插拔可替换,符合"核心不依赖外部"的架构目标。

六边形用"端口定义契约 + 适配器实现 + 依赖注入"隔离核心与基础设施,核心只依赖接口,基础设施成为可替换的适配器,实现依赖反转。

#

19. 微内核(插件化)架构中核心系统与插件的契约如何设计,为什么插件隔离与版本管理是最大挑战?

请解释微内核(插件化)架构,说明核心系统与插件的契约如何设计,以及为什么插件隔离与版本管理是最大挑战?

  • 核心与插件的扩展点契约设计
  • 插件隔离(类加载、异常隔离)
  • 版本管理挑战
  • 架构:核心系统(微内核)提供最小核心与扩展点(扩展点/接口契约),插件实现扩展点动态加载,为核心扩展功能。核心负责加载、调度、管理插件,不实现具体业务细节。
  • 契约设计:核心定义稳定的扩展点接口(如插件接口、SPI、扩展点描述),插件实现这些接口并通过清单(manifest)声明。契约要稳定、最小化、语义清晰,避免核心与插件互相依赖内部实现。通常用"核心定义扩展点 + 插件声明与注册"。
  • 插件隔离为何是最大挑战:插件需要运行时隔离——不同插件可能有冲突的类库/依赖版本,需用独立类加载器隔离,避免类冲突;插件崩溃不能拖垮核心与其他插件(异常隔离);插件加载/卸载不破坏核心。实现隔离需要类加载器体系、模块边界、依赖解析,复杂度高。
  • 版本管理挑战:插件有独立版本,核心需管理插件间依赖与版本兼容(插件 A 依赖插件 B 的某个版本),插件升级需兼容性检查;版本冲突会引发运行时错误。需版本化依赖解析、热插拔时的版本一致性。OSGi、Eclipse 的插件体系正是为此(Bundle 版本化 + 独立 ClassLoader)。

微内核的关键是"契约稳定 + 隔离可靠 + 版本兼容"。契约是解耦基础,隔离(类加载器)与版本管理是插件生态成熟化的最大挑战,OSGi/Eclipse 是典型实现。

#

20. ADR 中架构决策记录何时写、记录哪些内容,如何让决策上下文可追溯

请解释 ADR(Architecture Decision Record),说明架构决策记录何时写、记录哪些内容,以及如何让决策上下文可追溯?

  • ADR 的触发时机
  • 记录内容(背景、决策、后果、备选)
  • 可追溯性
  • ADR:架构决策记录,用结构化文档记录一次重要的架构决策,包括背景、决策、备选方案、后果,便于追溯与后续维护。
  • 何时写:在做重大架构决策时写(如选型、引入新组件、架构演进、关键权衡),而不是所有小决策。凡是"影响后续维护、难以更改、有多个备选"的决策都应记录。决策被讨论确定后尽快写。
  • 记录哪些内容:ADR 通常包含:状态(Proposed/Accepted/Deprecated)、背景(Context,为什么需要决策)、决策(Decision,选择了什么)、备选方案(Alternatives,考虑了哪些、为何放弃)、后果(Consequences,利弊、对后续的影响)、相关 ADR。例如"采用微服务拆分订单模块"。
  • 可追溯:ADR 用编号/标题组织,记录决策的上下文(触发原因、约束、备选),后续通过 ADR 可理解"为什么当初这么设计",支持回溯决策进程;ADR 随代码/文档版本管理,变更时更新状态,形成决策历史。

ADR 是"决策的文档化",记录背景、决策、备选、后果,让决策上下文可追溯,避免"为什么这么设计"失忆。时机是重大决策,内容结构化,状态可演进。

#

21. 架构坏味道中循环依赖、上帝服务与分布式单体如何识别,为什么比代码坏味道更难治理

请解释架构坏味道,说明循环依赖、上帝服务与分布式单体如何识别,以及为什么比代码坏味道更难治理?

  • 循环依赖、上帝服务、分布式单体的识别
  • 架构坏味道的隐性
  • 治理难度(跨模块、跨团队)
  • 架构坏味道:架构层面的设计问题,如循环依赖、上帝服务、分布式单体等,影响系统可维护性、可演化性。
  • 循环依赖:模块/服务 A 依赖 B、B 又依赖 A(或形成环),导致无法独立编译/部署/测试,问题相互纠缠。识别:依赖图分析发现有环(A→B→A),或两个服务互相调用。
  • 上帝服务(God Service):一个服务承担过多职责,成为"大而全"的巨婴服务,内部逻辑庞杂、难以拆分、成瓶颈。识别:服务职责过多、接口众多、改动频繁、团队协作冲突。
  • 分布式单体(Distributed Monolith):名义上拆成了微服务,但实际仍共享数据库、强耦合调用、同步依赖,导致"像单体一样无法独立部署/扩展",却承担了分布式复杂度。识别:服务间共享数据库、调用链冗长、无法独立发布、一个服务改动牵动全局。
  • 为什么比代码坏味道更难治理:代码坏味道局限在单个模块内,可局部重构;架构坏味道跨模块/跨服务/跨团队,改动涉及多方协调、数据迁移、接口契约,风险大、周期长,且往往隐性问题(不崩溃、只是难维护),难以量化和触发治理。需要架构治理、依赖治理、团队协作与长期演进。

架构坏味道是"系统级"的结构问题,识别靠依赖图/架构审视,治理要跨模块、跨团队、长期演进,比局部代码坏味道更难。循环依赖、上帝服务、分布式单体是典型代表。