5软件架构实践(第4版)

架构风格

常见的架构模式

分层管道-过滤器事件驱动微服务C4模型

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第5章 架构风格 (Architectural Styles)

一、导读

1.1 本章学习目标

在本章结束时,你将能够:

  • 理解架构风格的本质,区分风格与模式、战术的差异
  • 掌握常见架构风格,包括分层、客户端-服务器、管道-过滤器、事件驱动等
  • 分析风格特征,理解每种风格的组件、连接件和约束
  • 评估风格适用性,根据系统需求选择合适的架构风格
  • 组合多种风格,在复杂系统中灵活运用不同风格
  • 理解现代风格,掌握微服务、Serverless、事件溯源等新兴风格
  • 避免风格误区,识别常见的架构风格应用错误
  • 建立风格思维,从全局视角选择和组织系统结构

1.2 为什么本章如此重要

架构风格是软件架构的最高层抽象,它决定了系统的整体组织方式和组件交互模式。如果说战术是"招式",那么架构风格就是"拳法"——它提供了一套完整的结构和规则,指导整个系统的设计。

在实际项目中,架构风格的选择往往是第一个也是最关键的架构决策。一旦选定了风格,系统的很多特性就基本确定了:

  • 开发方式:团队如何组织和协作
  • 技术选型:可以使用哪些技术和框架
  • 部署策略:系统如何部署和运维
  • 扩展能力:系统如何应对增长
  • 变更成本:修改系统的难易程度

例如,选择单体架构意味着团队可以集中开发、统一部署,但可能面临扩展困难;选择微服务架构意味着团队可以独立开发和扩展,但需要处理分布式系统的复杂性。

架构风格的重要性还体现在以下几个方面:

首先,风格提供了经过验证的结构模板。每种风格都是多年实践经验的总结,它们已经被证明在特定类型的系统中有效。使用成熟的风格可以避免重复探索,降低项目风险。

其次,风格促进了沟通和理解。当说"这是一个分层架构"或"我们采用事件驱动"时,团队成员能够快速建立对系统结构的共同理解,减少沟通成本。

第三,风格影响了系统的全生命周期。从开发、测试到部署、运维,架构风格都会产生深远影响。选择一个不适合的风格可能导致整个生命周期的困难。

最后,风格不是非此即彼的。现代复杂系统往往组合多种风格,在不同层次和模块中应用不同的风格。理解各种风格的特点和适用场景,有助于做出更明智的组合决策。

本章将系统介绍常见的架构风格,包括它们的结构特征、优缺点、适用场景和典型应用。掌握这些风格,将为你的架构设计提供丰富的工具箱。

1.3 核心问题引导

在阅读本章时,请思考以下问题:

架构风格与设计模式的本质区别是什么?为什么需要两个层次的概念?

分层架构的每一层应该承担什么职责?层与层之间的依赖规则是什么?

客户端-服务器风格中,如何决定功能放在客户端还是服务器端?

管道-过滤器风格适合什么类型的问题?为什么不适合交互式应用?

事件驱动架构的核心优势是什么?它带来了哪些新的挑战?

微服务架构真的是银弹吗?它在什么情况下反而会成为负担?

如何在同一个系统中组合多种架构风格?有什么原则和指导?

架构风格的选择是否应该考虑团队因素?康威定律在这里如何体现?


二、核心概念详解

5.1 架构风格的定义

严格定义:

架构风格(Architectural Style)是一组系统组织的模式,它定义了:

  • 系统中允许的组件类型
  • 组件之间的连接件类型
  • 组件组合的拓扑结构约束
  • 数据和控制流的语义

通俗地说,架构风格就像建筑风格一样,提供了一套设计规则和美学标准。哥特式建筑有尖拱、飞扶壁和彩色玻璃窗;同样,分层架构有明确的层次划分和单向依赖规则。

风格的要素

每个架构风格由以下要素定义:

组件(Components):风格允许的计算或存储单元

- 例如:分层风格中的"层",管道-过滤器风格中的"过滤器"

连接件(Connectors):风格允许的交互机制

- 例如:分层风格中的"过程调用",事件驱动风格中的"事件总线"

约束(Constraints):风格规定的结构规则

- 例如:分层风格中"只能调用相邻下层",管道-过滤器中"无状态过滤器"

语义(Semantics):组件和连接件的交互含义

- 例如:发布-订阅中"发布者不知道订阅者"

风格 vs 模式 vs 战术

维度架构风格架构模式架构战术
粒度系统级子系统/模块级组件/功能级
抽象度最高中等最低
适用范围整个系统特定问题特定质量属性
示例微服务、分层MVC、CQRS缓存、冗余

关系:

  • 一个系统采用一种架构风格
  • 系统内部的不同模块可以采用不同的架构模式
  • 模式和风格的实现依赖于具体的架构战术

5.2 分层架构(Layered Architecture)

分层架构是最经典、最广泛使用的架构风格之一。

结构特征

  • 组件:层(Layer),每层有明确的职责
  • 连接件:层间调用(通常是过程调用)
  • 约束:

- 层按顺序排列

- 每层只能使用下层提供的服务

- 上层对下层透明

典型分层

┌─────────────────────┐
│   Presentation      │  表现层:用户界面
├─────────────────────┤
│   Application       │  应用层:业务流程
├─────────────────────┤
│   Domain/Business   │  领域层:核心业务逻辑
├─────────────────────┤
│   Infrastructure    │  基础设施层:数据访问、外部服务
└─────────────────────┘

直觉解释

通俗地说,分层架构就像公司的组织架构:CEO 不直接管一线员工,而是通过管理层级逐级传达。每一层只与上下层打交道,不需要了解其他层的细节。

优点

  • 关注点分离:每层专注于自己的职责
  • 可维护性:修改一层不影响其他层
  • 可测试性:可以单独测试每一层
  • 易于理解:结构清晰,新人容易上手
  • 技术隔离:可以在不同层使用不同技术

缺点

  • 性能开销:请求需要经过多层传递
  • 过度工程:简单系统可能不需要这么多层
  • 层泄漏:下层细节可能泄漏到上层
  • 僵化:严格的分层可能限制灵活性

适用场景

  • 企业信息系统
  • Web 应用程序
  • 需要清晰职责分离的系统
  • 团队按技能分工的组织

变体

  • 严格分层:只能调用直接下层
  • 宽松分层:可以调用任何下层
  • 开放分层:允许跨层调用(但有控制)

代码示例

java
// 表现层
@RestController
@RequestMapping("/orders")
public class OrderController {
    private final OrderService orderService;
    
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
    
    @PostMapping
    public OrderResponse createOrder(@RequestBody CreateOrderRequest request) {
        return orderService.createOrder(request);
    }
}

// 应用层
@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentClient paymentClient;
    
    public Order createOrder(CreateOrderRequest request) {
        // 业务编排
        Order order = convertToOrder(request);
        order = orderRepository.save(order);
        paymentClient.charge(order.getTotalAmount());
        return order;
    }
}

// 领域层
@Entity
public class Order {
    private Long id;
    private List<OrderItem> items;
    private BigDecimal totalAmount;
    
    public BigDecimal calculateTotal() {
        return items.stream()
            .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

// 基础设施层
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Spring Data 自动生成实现
}

5.3 客户端-服务器架构(Client-Server Architecture)

客户端-服务器架构是分布式系统的基础风格。

结构特征

  • 组件:

- 客户端(Client):发起请求的一方

- 服务器(Server):提供服务的一方

  • 连接件:网络协议(HTTP、RPC、WebSocket 等)
  • 约束:

- 客户端主动发起请求

- 服务器被动响应请求

- 服务器可以同时服务多个客户端

直觉解释

通俗地说,就像餐厅里顾客(客户端)点菜,厨房(服务器)做菜。顾客提出需求,厨房满足需求。厨房可以同时服务多桌顾客。

变体

两层架构(2-Tier)

- 客户端直接与数据库交互

- 例如:桌面应用 + 数据库

三层架构(3-Tier)

- 客户端 → 应用服务器 → 数据库服务器

- 例如:Web 浏览器 → Web 服务器 → 数据库

N 层架构

- 更多层次的服务器

- 例如:负载均衡器 → Web 服务器 → 应用服务器 → 数据库

优点

  • 集中管理:数据和业务逻辑集中在服务器
  • 安全性:敏感数据不暴露在客户端
  • 可扩展性:服务器可以独立扩展
  • 维护便利:更新服务器即可,无需更新所有客户端

缺点

  • 单点故障:服务器宕机影响所有客户端
  • 性能瓶颈:服务器可能成为瓶颈
  • 网络依赖:需要稳定的网络连接
  • 扩展限制:垂直扩展有上限

适用场景

  • Web 应用
  • 移动应用后端
  • 企业信息系统
  • 需要集中数据管理的系统

现代演进

  • RESTful API:标准化的 HTTP 接口
  • GraphQL:灵活的查询语言
  • gRPC:高性能的 RPC 框架

5.4 管道-过滤器架构(Pipe-and-Filter Architecture)

管道-过滤器风格将数据处理分解为一系列独立的处理步骤。

结构特征

  • 组件:过滤器(Filter),执行特定的数据转换
  • 连接件:管道(Pipe),在过滤器之间传输数据
  • 约束:

- 过滤器应该是无状态的

- 过滤器之间不直接通信,只通过管道

- 数据流是单向的

直觉解释

通俗地说,就像工厂的流水线。原材料进入流水线,经过一道道工序加工,每一步只做一件事,最后产出成品。每道工序不知道前后工序的细节,只关心自己的输入和输出。

典型应用

  • 编译器:词法分析 → 语法分析 → 语义分析 → 代码生成
  • 图像处理:读取 → 滤波 → 增强 → 压缩 → 保存
  • 日志处理:收集 → 过滤 → 聚合 → 分析 → 存储

优点

  • 高内聚低耦合:每个过滤器职责单一
  • 可复用:过滤器可以组合使用
  • 可并行:过滤器可以并行执行
  • 易于理解:数据流清晰可见

缺点

  • 不适合交互:难以处理用户交互
  • 数据传输开销:数据需要在过滤器间复制
  • 错误处理复杂:某个过滤器失败影响整个管道
  • 共享状态困难:过滤器无状态限制了设计

适用场景

  • 批处理任务
  • 数据转换和处理
  • 信号处理
  • 编译器和分析器

代码示例

java
// 过滤器接口
interface Filter<T> {
    T process(T input);
}

// 具体过滤器
class ToUpperCaseFilter implements Filter<String> {
    @Override
    public String process(String input) {
        return input.toUpperCase();
    }
}

class RemoveSpacesFilter implements Filter<String> {
    @Override
    public String process(String input) {
        return input.replaceAll("\\s+", "");
    }
}

class ReverseFilter implements Filter<String> {
    @Override
    public String process(String input) {
        return new StringBuilder(input).reverse().toString();
    }
}

// 管道
class Pipeline<T> {
    private final List<Filter<T>> filters = new ArrayList<>();
    
    public Pipeline<T> addFilter(Filter<T> filter) {
        filters.add(filter);
        return this;
    }
    
    public T execute(T input) {
        T result = input;
        for (Filter<T> filter : filters) {
            result = filter.process(result);
        }
        return result;
    }
}

// 使用
Pipeline<String> pipeline = new Pipeline<>();
pipeline.addFilter(new ToUpperCaseFilter())
        .addFilter(new RemoveSpacesFilter())
        .addFilter(new ReverseFilter());

String result = pipeline.execute("hello world");
// 结果: "DLROWOLLEH"

5.5 事件驱动架构(Event-Driven Architecture)

事件驱动架构基于事件的产生、检测和消费来组织系统。

结构特征

  • 组件:

- 事件生产者(Producer):生成事件

- 事件消费者(Consumer):处理事件

- 事件通道(Channel):传输事件(如消息队列、事件总线)

  • 连接件:异步消息传递
  • 约束:

- 生产者和消费者解耦

- 事件是过去发生的事实

- 消费者不知道生产者

直觉解释

通俗地说,就像广播电台。电台(生产者)播放节目,听众(消费者)收听。电台不知道谁在听,听众也不知道其他听众是谁。有人想听就打开收音机,不想听就关掉。

模式变体

发布-订阅(Pub/Sub)

- 生产者发布事件到主题

- 消费者订阅感兴趣的主题

- 一个事件可以被多个消费者处理

事件溯源(Event Sourcing)

- 状态变化记录为事件序列

- 当前状态由重放事件推导

- 完整的历史记录

CQRS(命令查询责任分离)

- 写操作(命令)和读操作(查询)分离

- 写侧生成事件,读侧消费事件更新视图

优点

  • 松耦合:生产者和消费者互不依赖
  • 可扩展性:可以轻松添加新的消费者
  • 弹性:消费者可以异步处理,缓冲峰值负载
  • 审计性:事件日志提供完整的操作历史

缺点

  • 复杂性:异步处理增加调试难度
  • 一致性:最终一致性可能导致临时不一致
  • 顺序保证:确保事件顺序需要额外机制
  • 重复处理:需要幂等性设计

适用场景

  • 实时数据处理
  • 微服务间通信
  • 用户活动追踪
  • 工作流和业务流程

代码示例

java
// 事件定义
record OrderCreatedEvent(String orderId, String userId, BigDecimal amount) {}
record OrderPaidEvent(String orderId, String paymentId) {}

// 事件总线
class EventBus {
    private final Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();
    
    public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
                .add(handler::accept);
    }
    
    public <T> void publish(T event) {
        List<Consumer<Object>> eventHandlers = handlers.getOrDefault(event.getClass(), Collections.emptyList());
        for (Consumer<Object> handler : eventHandlers) {
            handler.accept(event);
        }
    }
}

// 事件处理器
class OrderEventHandler {
    private final EventBus eventBus;
    
    public OrderEventHandler(EventBus eventBus) {
        this.eventBus = eventBus;
        
        // 订阅事件
        eventBus.subscribe(OrderCreatedEvent.class, this::handleOrderCreated);
        eventBus.subscribe(OrderPaidEvent.class, this::handleOrderPaid);
    }
    
    private void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("Order created: " + event.orderId());
        // 发送确认邮件、初始化库存预留等
    }
    
    private void handleOrderPaid(OrderPaidEvent event) {
        System.out.println("Order paid: " + event.orderId());
        // 更新订单状态、通知物流等
    }
}

// 使用
EventBus eventBus = new EventBus();
OrderEventHandler handler = new OrderEventHandler(eventBus);

// 发布事件
eventBus.publish(new OrderCreatedEvent("ORD-1", "USER-1", new BigDecimal("99.99")));
eventBus.publish(new OrderPaidEvent("ORD-1", "PAY-1"));

5.6 微服务架构(Microservices Architecture)

微服务架构将系统拆分为一组小型、独立的服务。

结构特征

  • 组件:服务(Service),每个服务:

- 围绕业务能力组织

- 独立部署

- 拥有自己的数据存储

- 通过轻量级协议通信

  • 连接件:HTTP/REST、gRPC、消息队列
  • 约束:

- 服务边界清晰

- 去中心化治理

- 基础设施自动化

直觉解释

通俗地说,就像商场里的各个店铺。每家店独立经营,有自己的库存和收银,但共享商场的基础设施(水电、安保)。顾客可以在不同店铺购物,店铺之间互不干扰。一家店出问题不影响其他店营业。

优点

  • 独立部署:服务可以独立发布和扩展
  • 技术多样性:不同服务可以用不同技术栈
  • 团队自治:小团队可以独立负责服务
  • 故障隔离:单个服务故障不影响整体

缺点

  • 分布式复杂性:网络延迟、部分失败、数据一致性
  • 运维负担:需要监控、日志、追踪等基础设施
  • 测试困难:集成测试和端到端测试复杂
  • 事务管理:跨服务事务需要特殊处理

适用场景

  • 大型复杂系统
  • 多团队协作
  • 需要快速迭代的业务
  • 不同模块有不同扩展需求

关键模式

  • API 网关:统一的入口点
  • 服务发现:动态定位服务实例
  • 熔断器:防止级联故障
  • 配置中心:集中管理配置

5.7 其他常见架构风格

5.7.1 模型-视图-控制器(MVC)

  • 组件:模型(数据)、视图(展示)、控制器(逻辑)
  • 适用:用户界面应用
  • 优点:关注点分离,便于测试

5.7.2 黑板架构(Blackboard Architecture)

  • 组件:黑板(共享数据)、知识源(处理单元)、控制组件
  • 适用:AI 系统、问题求解
  • 特点:多个专家协同解决复杂问题

5.7.3 解释器架构(Interpreter Architecture)

  • 组件:解释引擎、指令集、状态存储
  • 适用:脚本语言、规则引擎
  • 特点:灵活但性能较低

5.7.4 Serverless 架构

  • 组件:函数(Function)、触发器、云服务
  • 适用:事件驱动、突发负载
  • 优点:无需管理服务器,按需付费
  • 缺点:冷启动延迟、供应商锁定

5.8 架构风格的组合

实际系统中很少只使用单一风格,通常是多种风格的组合。

组合原则

分层 + 其他风格

- 在每层内部使用不同的风格

- 例如:表现层用 MVC,业务层用领域驱动,数据层用仓库模式

宏观 + 微观

- 系统级用一种风格,模块级用另一种

- 例如:整体是微服务,每个服务内部是分层的

边界清晰

- 不同风格之间有明确的边界

- 避免风格混杂导致混乱

组合示例

系统级:微服务架构
  ├─ 订单服务:分层架构 + 事件驱动
  ├─ 用户服务:分层架构 + REST API
  ├─ 推荐服务:管道-过滤器 + 机器学习
  └─ API 网关:客户端-服务器

三、程序员的视角

3.1 在架构设计中的应用

场景一:新项目选型

启动新项目时,根据需求选择合适的架构风格:

需求分析:
- 团队规模:5 人
- 预期用户:初期 1000,目标 10 万
- 业务复杂度:中等
- 上线时间:2 个月

决策:
- 初期:单体分层架构(快速开发)
- 后期:根据瓶颈逐步拆分微服务

理由:
- 小团队不适合微服务的运维复杂度
- 分层架构足够应对中等复杂度
- 保留未来演进的空间

场景二:遗留系统改造

改造老旧系统时,渐进式引入新风格:

现状:
- 10 年历史的单体应用
- 代码耦合严重
- 部署困难

改造策略:
1. 绞杀者植物模式:新功能用微服务实现
2. 逐步剥离:将独立模块拆分为服务
3. 最终目标:核心保留单体,外围微服务化

注意:
- 不要一次性重写
- 保持向后兼容
- 建立适配层隔离新旧系统

场景三:性能优化

通过改变架构风格改善性能:

问题:报表生成慢,阻塞主线程

方案:引入管道-过滤器风格
- 将报表生成拆分为:数据提取 → 数据转换 → 格式化 → 导出
- 每个步骤独立执行,可以并行
- 使用消息队列异步处理

效果:
- 主线程不被阻塞
- 可以水平扩展处理节点
- 用户可以提交后稍后获取结果

3.2 常见陷阱与误区

陷阱一:盲目追求微服务

表现:不管系统规模和复杂度,一律采用微服务架构。

后果:

  • 运维复杂度剧增
  • 分布式问题频发
  • 团队精力分散在基础设施上

避免方法:

  • 评估团队能力和系统需求
  • 从单体开始,按需拆分
  • 记住:微服务不是银弹

陷阱二:分层过度

表现:为简单系统设计过多的层次。

例如:

  • Controller → Service → Manager → Handler → Repository
  • 每层只是透传,没有实际逻辑

后果:

  • 代码量膨胀
  • 调试困难
  • 新人困惑

避免方法:

  • 遵循 KISS 原则
  • 只有当层有明确职责时才创建
  • 定期重构合并冗余层

陷阱三:风格混杂

表现:在同一模块中混用多种风格,没有清晰边界。

例如:

  • 既有同步调用又有异步事件
  • 既有 REST 又有 RPC
  • 既有集中式又有分布式

后果:

  • 系统行为不可预测
  • 调试和维护困难
  • 新人难以理解

避免方法:

  • 明确每个模块的风格
  • 在边界处进行风格转换
  • 文档化风格选择的原因

陷阱四:忽视风格的约束

表现:采用某种风格但不遵守其约束。

例如:

  • 分层架构中跨层调用
  • 管道-过滤器中过滤器有状态
  • 事件驱动中消费者依赖生产者

后果:

  • 失去风格带来的好处
  • 引入意想不到的问题
  • 架构名存实亡

避免方法:

  • 深入理解风格的约束
  • 通过代码审查确保合规
  • 使用工具检测违规

3.3 与其他层级的联系

与团队的联系

康威定律:系统设计受限于生产它的组织的沟通结构。

  • 小团队 → 单体或少数服务
  • 大团队 → 微服务或多团队架构
  • 跨地域团队 → 松耦合的服务架构

与技术的联系

不同风格适合不同的技术栈:

  • 分层架构 → Spring Boot、ASP.NET
  • 事件驱动 → Kafka、RabbitMQ
  • 微服务 → Docker、Kubernetes
  • Serverless → AWS Lambda、Azure Functions

与业务的联系

业务特征影响风格选择:

  • 稳定业务 → 成熟稳重的风格(分层)
  • 创新业务 → 灵活快速的风格(微服务)
  • 实时业务 → 事件驱动
  • 批处理业务 → 管道-过滤器

四、代码实战

4.1 示例一:分层架构的完整实现

下面我们用 Java 展示一个完整的分层架构示例。

java
/**
 * 电商系统分层架构示例
 * 
 * 层次:
 * 1. 表现层:REST API
 * 2. 应用层:业务编排
 * 3. 领域层:核心业务逻辑
 * 4. 基础设施层:数据访问
 */

// ==================== 领域层 ====================

package com.example.ecommerce.domain;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Order {
    private final String id;
    private final String customerId;
    private final List<OrderItem> items;
    private OrderStatus status;
    private BigDecimal totalAmount;
    
    public Order(String id, String customerId) {
        this.id = id;
        this.customerId = customerId;
        this.items = new ArrayList<>();
        this.status = OrderStatus.CREATED;
        this.totalAmount = BigDecimal.ZERO;
    }
    
    public void addItem(Product product, int quantity) {
        if (quantity <= 0) {
            throw new IllegalArgumentException("Quantity must be positive");
        }
        items.add(new OrderItem(product.getId(), product.getName(), 
                               product.getPrice(), quantity));
        recalculateTotal();
    }
    
    private void recalculateTotal() {
        this.totalAmount = items.stream()
            .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    public void confirm() {
        if (status != OrderStatus.CREATED) {
            throw new IllegalStateException("Order cannot be confirmed");
        }
        if (items.isEmpty()) {
            throw new IllegalStateException("Order must have items");
        }
        this.status = OrderStatus.CONFIRMED;
    }
    
    public void cancel() {
        if (status == OrderStatus.SHIPPED || status == OrderStatus.DELIVERED) {
            throw new IllegalStateException("Cannot cancel shipped/delivered order");
        }
        this.status = OrderStatus.CANCELLED;
    }
    
    // Getters
    public String getId() { return id; }
    public String getCustomerId() { return customerId; }
    public List<OrderItem> getItems() { return Collections.unmodifiableList(items); }
    public OrderStatus getStatus() { return status; }
    public BigDecimal getTotalAmount() { return totalAmount; }
}

class OrderItem {
    private final String productId;
    private final String productName;
    private final BigDecimal price;
    private final int quantity;
    
    public OrderItem(String productId, String productName, BigDecimal price, int quantity) {
        this.productId = productId;
        this.productName = productName;
        this.price = price;
        this.quantity = quantity;
    }
    
    public String getProductId() { return productId; }
    public String getProductName() { return productName; }
    public BigDecimal getPrice() { return price; }
    public int getQuantity() { return quantity; }
}

class Product {
    private final String id;
    private final String name;
    private final BigDecimal price;
    private int stock;
    
    public Product(String id, String name, BigDecimal price, int stock) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
    }
    
    public boolean isAvailable(int quantity) {
        return stock >= quantity;
    }
    
    public void reduceStock(int quantity) {
        if (!isAvailable(quantity)) {
            throw new IllegalArgumentException("Insufficient stock");
        }
        this.stock -= quantity;
    }
    
    public String getId() { return id; }
    public String getName() { return name; }
    public BigDecimal getPrice() { return price; }
    public int getStock() { return stock; }
}

enum OrderStatus {
    CREATED, CONFIRMED, PAID, SHIPPED, DELIVERED, CANCELLED
}

// ==================== 基础设施层 ====================

package com.example.ecommerce.infrastructure;

import com.example.ecommerce.domain.Order;
import com.example.ecommerce.domain.Product;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public interface OrderRepository {
    void save(Order order);
    Optional<Order> findById(String id);
    List<Order> findByCustomerId(String customerId);
}

public interface ProductRepository {
    Optional<Product> findById(String id);
    void update(Product product);
}

public class InMemoryOrderRepository implements OrderRepository {
    private final Map<String, Order> orders = new ConcurrentHashMap<>();
    
    @Override
    public void save(Order order) {
        orders.put(order.getId(), order);
    }
    
    @Override
    public Optional<Order> findById(String id) {
        return Optional.ofNullable(orders.get(id));
    }
    
    @Override
    public List<Order> findByCustomerId(String customerId) {
        return orders.values().stream()
            .filter(o -> o.getCustomerId().equals(customerId))
            .toList();
    }
}

public class InMemoryProductRepository implements ProductRepository {
    private final Map<String, Product> products = new ConcurrentHashMap<>();
    
    public InMemoryProductRepository() {
        // 初始化一些产品
        products.put("P1", new Product("P1", "Laptop", new BigDecimal("999.99"), 100));
        products.put("P2", new Product("P2", "Mouse", new BigDecimal("29.99"), 500));
        products.put("P3", new Product("P3", "Keyboard", new BigDecimal("59.99"), 300));
    }
    
    @Override
    public Optional<Product> findById(String id) {
        return Optional.ofNullable(products.get(id));
    }
    
    @Override
    public void update(Product product) {
        products.put(product.getId(), product);
    }
}

// ==================== 应用层 ====================

package com.example.ecommerce.application;

import com.example.ecommerce.domain.*;
import com.example.ecommerce.infrastructure.*;

import java.util.UUID;

public class OrderService {
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    
    public OrderService(OrderRepository orderRepository, ProductRepository productRepository) {
        this.orderRepository = orderRepository;
        this.productRepository = productRepository;
    }
    
    public Order createOrder(String customerId, List<OrderItemRequest> items) {
        String orderId = UUID.randomUUID().toString();
        Order order = new Order(orderId, customerId);
        
        for (OrderItemRequest itemReq : items) {
            Product product = productRepository.findById(itemReq.productId())
                .orElseThrow(() -> new IllegalArgumentException("Product not found: " + itemReq.productId()));
            
            if (!product.isAvailable(itemReq.quantity())) {
                throw new IllegalArgumentException("Insufficient stock for: " + product.getName());
            }
            
            product.reduceStock(itemReq.quantity());
            productRepository.update(product);
            
            order.addItem(product, itemReq.quantity());
        }
        
        order.confirm();
        orderRepository.save(order);
        
        return order;
    }
    
    public Order getOrder(String orderId) {
        return orderRepository.findById(orderId)
            .orElseThrow(() -> new IllegalArgumentException("Order not found: " + orderId));
    }
    
    public void cancelOrder(String orderId) {
        Order order = getOrder(orderId);
        order.cancel();
        orderRepository.save(order);
    }
}

record OrderItemRequest(String productId, int quantity) {}

// ==================== 表现层 ====================

package com.example.ecommerce.presentation;

import com.example.ecommerce.application.OrderService;
import com.example.ecommerce.application.OrderItemRequest;
import com.example.ecommerce.domain.Order;

import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private final OrderService orderService;
    
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
    
    @PostMapping
    public Map<String, Object> createOrder(@RequestBody CreateOrderRequest request) {
        Order order = orderService.createOrder(
            request.customerId(),
            request.items().stream()
                .map(item -> new OrderItemRequest(item.productId(), item.quantity()))
                .toList()
        );
        
        return Map.of(
            "orderId", order.getId(),
            "totalAmount", order.getTotalAmount(),
            "status", order.getStatus().toString()
        );
    }
    
    @GetMapping("/{orderId}")
    public Map<String, Object> getOrder(@PathVariable String orderId) {
        Order order = orderService.getOrder(orderId);
        return Map.of(
            "orderId", order.getId(),
            "customerId", order.getCustomerId(),
            "items", order.getItems().stream()
                .map(item -> Map.of(
                    "productId", item.getProductId(),
                    "productName", item.getProductName(),
                    "price", item.getPrice(),
                    "quantity", item.getQuantity()
                ))
                .toList(),
            "totalAmount", order.getTotalAmount(),
            "status", order.getStatus().toString()
        );
    }
    
    @PostMapping("/{orderId}/cancel")
    public Map<String, String> cancelOrder(@PathVariable String orderId) {
        orderService.cancelOrder(orderId);
        return Map.of("status", "cancelled");
    }
}

record CreateOrderRequest(String customerId, List<ItemRequest> items) {}
record ItemRequest(String productId, int quantity) {}

这个示例展示了分层架构的完整实现:

领域层:核心业务实体和逻辑

基础设施层:数据访问接口和实现

应用层:业务编排和用例

表现层:REST API 接口

4.2 示例二:事件驱动架构的实现

下面我们用 Python 展示事件驱动架构的实现。

python
"""
事件驱动架构示例
在线书店的订单处理流程
"""

from dataclasses import dataclass
from typing import Callable, List, Dict
from datetime import datetime
import json
import time


# ==================== 事件定义 ====================

@dataclass
class Event:
    """基础事件类"""
    event_id: str
    timestamp: datetime
    source: str
    
    def to_json(self):
        return json.dumps({
            "event_id": self.event_id,
            "timestamp": self.timestamp.isoformat(),
            "source": self.source
        })


@dataclass
class OrderPlacedEvent(Event):
    order_id: str
    customer_id: str
    items: List[Dict]
    total_amount: float


@dataclass
class PaymentProcessedEvent(Event):
    order_id: str
    payment_id: str
    amount: float
    status: str


@dataclass
class InventoryReservedEvent(Event):
    order_id: str
    reservations: List[Dict]


@dataclass
class OrderShippedEvent(Event):
    order_id: str
    tracking_number: str
    carrier: str


# ==================== 事件总线 ====================

class EventBus:
    """
    简单的事件总线实现
    支持发布-订阅模式
    """
    
    def __init__(self):
        self._subscribers: Dict[type, List[Callable]] = {}
        self._event_log: List[Event] = []
    
    def subscribe(self, event_type: type, handler: Callable):
        """订阅特定类型的事件"""
        if event_type not in self._subscribers:
            self._subscribers[event_type] = []
        self._subscribers[event_type].append(handler)
        print(f"Subscribed handler to {event_type.__name__}")
    
    def publish(self, event: Event):
        """发布事件"""
        self._event_log.append(event)
        print(f"\n📢 Publishing event: {type(event).__name__}")
        print(f"   Event ID: {event.event_id}")
        
        # 找到匹配的订阅者
        handlers = self._subscribers.get(type(event), [])
        
        for handler in handlers:
            try:
                handler(event)
                print(f"   ✓ Handler executed successfully")
            except Exception as e:
                print(f"   ✗ Handler failed: {e}")
        
        if not handlers:
            print(f"   ⚠ No subscribers for this event")


# ==================== 事件处理器 ====================

class PaymentService:
    """支付服务"""
    
    def __init__(self, event_bus: EventBus):
        self.event_bus = event_bus
        # 订阅订单创建事件
        event_bus.subscribe(OrderPlacedEvent, self.handle_order_placed)
    
    def handle_order_placed(self, event: OrderPlacedEvent):
        """处理订单创建事件,发起支付"""
        print(f"   Processing payment for order {event.order_id}")
        
        # 模拟支付处理
        payment_id = f"PAY-{event.order_id.split('-')[1]}"
        
        # 发布支付处理完成事件
        payment_event = PaymentProcessedEvent(
            event_id=f"EVT-PAY-{int(time.time())}",
            timestamp=datetime.now(),
            source="payment-service",
            order_id=event.order_id,
            payment_id=payment_id,
            amount=event.total_amount,
            status="completed"
        )
        self.event_bus.publish(payment_event)


class InventoryService:
    """库存服务"""
    
    def __init__(self, event_bus: EventBus):
        self.event_bus = event_bus
        self._inventory = {
            "BOOK-1": {"title": "Python Programming", "stock": 100},
            "BOOK-2": {"title": "Java Basics", "stock": 50},
            "BOOK-3": {"title": "Web Development", "stock": 75},
        }
        # 订阅订单创建事件
        event_bus.subscribe(OrderPlacedEvent, self.handle_order_placed)
    
    def handle_order_placed(self, event: OrderPlacedEvent):
        """处理订单创建事件,预留库存"""
        print(f"   Reserving inventory for order {event.order_id}")
        
        reservations = []
        for item in event.items:
            book_id = item["book_id"]
            quantity = item["quantity"]
            
            if book_id in self._inventory:
                if self._inventory[book_id]["stock"] >= quantity:
                    self._inventory[book_id]["stock"] -= quantity
                    reservations.append({
                        "book_id": book_id,
                        "title": self._inventory[book_id]["title"],
                        "quantity": quantity
                    })
                    print(f"   Reserved {quantity} x {book_id}")
                else:
                    raise ValueError(f"Insufficient stock for {book_id}")
        
        # 发布库存预留完成事件
        inventory_event = InventoryReservedEvent(
            event_id=f"EVT-INV-{int(time.time())}",
            timestamp=datetime.now(),
            source="inventory-service",
            order_id=event.order_id,
            reservations=reservations
        )
        self.event_bus.publish(inventory_event)


class ShippingService:
    """物流服务"""
    
    def __init__(self, event_bus: EventBus):
        self.event_bus = event_bus
        # 订阅支付完成事件
        event_bus.subscribe(PaymentProcessedEvent, self.handle_payment_completed)
    
    def handle_payment_completed(self, event: PaymentProcessedEvent):
        """处理支付完成事件,安排发货"""
        if event.status != "completed":
            print(f"   Payment not completed, skipping shipping")
            return
        
        print(f"   Arranging shipping for order {event.order_id}")
        
        # 模拟生成运单号
        tracking_number = f"TRACK-{event.order_id.split('-')[1]}"
        
        # 发布发货事件
        shipping_event = OrderShippedEvent(
            event_id=f"EVT-SHIP-{int(time.time())}",
            timestamp=datetime.now(),
            source="shipping-service",
            order_id=event.order_id,
            tracking_number=tracking_number,
            carrier="Express Delivery"
        )
        self.event_bus.publish(shipping_event)


class NotificationService:
    """通知服务"""
    
    def __init__(self, event_bus: EventBus):
        self.event_bus = event_bus
        # 订阅多个事件
        event_bus.subscribe(OrderPlacedEvent, self.handle_order_placed)
        event_bus.subscribe(OrderShippedEvent, self.handle_order_shipped)
    
    def handle_order_placed(self, event: OrderPlacedEvent):
        """发送订单确认通知"""
        print(f"   Sending order confirmation to customer {event.customer_id}")
        print(f"   Order: {event.order_id}, Total: ${event.total_amount}")
    
    def handle_order_shipped(self, event: OrderShippedEvent):
        """发送发货通知"""
        print(f"   Sending shipping notification for order {event.order_id}")
        print(f"   Tracking: {event.tracking_number}, Carrier: {event.carrier}")


# ==================== 测试 ====================

def main():
    print("=" * 60)
    print("事件驱动架构演示 - 在线书店订单处理")
    print("=" * 60)
    
    # 创建事件总线
    event_bus = EventBus()
    
    # 创建服务(自动订阅事件)
    payment_service = PaymentService(event_bus)
    inventory_service = InventoryService(event_bus)
    shipping_service = ShippingService(event_bus)
    notification_service = NotificationService(event_bus)
    
    # 模拟用户下单
    print("\n" + "=" * 60)
    print("用户下单...")
    print("=" * 60)
    
    order_event = OrderPlacedEvent(
        event_id="EVT-ORDER-1",
        timestamp=datetime.now(),
        source="web-store",
        order_id="ORD-001",
        customer_id="CUST-123",
        items=[
            {"book_id": "BOOK-1", "quantity": 2},
            {"book_id": "BOOK-3", "quantity": 1}
        ],
        total_amount=59.97
    )
    
    # 发布订单创建事件
    # 这会触发一系列连锁反应:
    # 1. PaymentService 处理支付
    # 2. InventoryService 预留库存
    # 3. PaymentService 发布支付完成事件
    # 4. InventoryService 发布库存预留事件
    # 5. ShippingService 收到支付完成事件,安排发货
    # 6. NotificationService 发送通知
    event_bus.publish(order_event)
    
    print("\n" + "=" * 60)
    print("订单处理完成")
    print("=" * 60)


if __name__ == "__main__":
    main()

这个示例展示了事件驱动架构的核心特征:

事件定义:结构化的事件数据

事件总线:发布-订阅机制

事件处理器:独立的服务,订阅感兴趣的事件

事件链:一个事件触发后续事件,形成处理流程


五、深入思考

5.1 历史背景

架构风格的发展反映了软件工程理念的演变。

早期阶段(1960s-1970s)

  • 1960s:单体架构主导,程序是一个整体
  • 1970s:结构化编程兴起,模块化思想萌芽
  • 1972 年:David Parnas 提出信息隐藏和模块化原则

经典风格形成(1980s-1990s)

  • 1980s:客户端-服务器架构流行,PC 普及
  • 1988 年:MVC 模式在小talk 中诞生
  • 1990s:分层架构成为企业系统标准
  • 1995 年:Mary Shaw 和 David Garlan 系统化架构风格研究
  • 1996 年:《Software Architecture in Practice》出版

互联网时代(2000s)

  • 2000s 初:SOA(面向服务架构)兴起
  • 2004 年:REST 论文发表,Web API 标准化
  • 2006 年:AWS 云服务推出,云原生架构萌芽
  • 2000s 末:事件驱动架构在实时系统中普及

现代发展(2010s 至今)

  • 2011 年:微服务概念首次提出
  • 2014 年:Martin Fowler 发表微服务标志性文章
  • 2014 年:Docker 发布,容器化普及
  • 2015 年:Kubernetes 开源,容器编排标准化
  • 2010s 末:Serverless 架构兴起
  • 2020s:边缘计算、Service Mesh 等新风格涌现

关键人物

  • Mary Shaw, David Garlan:架构风格研究的奠基人
  • Roy Fielding:REST 架构风格的提出者
  • Martin Fowler:微服务和持续交付的倡导者
  • Len Bass, Paul Clements, Rick Kazman:架构实践的先驱

5.2 未解决的问题

风格的自动选择

能否根据系统需求自动推荐合适的架构风格?这需要形式化的需求分析和风格匹配算法。

风格的演化路径

系统从一种风格迁移到另一种风格的最佳路径是什么?例如,从单体到微服务的渐进式迁移策略。

混合风格的理论

如何系统地理解和设计混合多种风格的架构?目前主要依靠经验,缺乏理论指导。

新兴风格的规范化

Serverless、Edge Computing 等新风格还在发展中,它们的最佳实践和约束尚未完全确立。

风格与 AI 的结合

AI 系统(如机器学习管道)是否需要新的架构风格?现有的风格是否足够?

5.3 延伸阅读

《Software Architecture in Practice》(第 4 版) - Len Bass, Paul Clements, Rick Kazman

本书对架构风格有详细的分类和讲解。

《Fundamentals of Software Architecture》 - Mark Richards, Neal Ford

从实践角度讲解各种架构风格的权衡。

《Building Microservices》 - Sam Newman

微服务架构的权威指南。

《Enterprise Integration Patterns》 - Gregor Hohpe, Bobby Woolf

事件驱动和消息传递模式的经典著作。

"Architectural Styles and the Design of Network-based Software Architectures"(论文) - Roy Fielding

REST 架构风格的原始论文。


六、实验笔记

实验一:架构风格分析

目标:分析真实系统的架构风格。

步骤:

选择一个开源项目(如 Spring Boot、Django、Kubernetes 等)

阅读项目的架构文档和代码结构

识别系统采用的架构风格

分析:

a. 为什么选择这种风格?

b. 风格的组件和连接件是什么?

c. 是否遵守了风格的约束?

d. 风格的优点和缺点在项目中如何体现?

如果有多种风格组合,分析组合的方式和原因

提出改进建议

预期结果:

  • 一份架构风格分析报告
  • 系统架构图
  • 风格选择的合理性评估
  • 改进建议

思考题:

如果重新设计,会选择不同的风格吗?为什么?

项目的团队结构如何影响了架构风格的选择?

风格是否随着项目演进而变化?变化的驱动力是什么?

实验二:风格迁移设计

目标:设计从一种架构风格迁移到另一种的方案。

场景:将一个单体电商系统迁移到微服务架构。

步骤:

分析单体系统的结构和模块

确定目标微服务的边界

设计迁移策略:

a. 绞杀者植物模式

b. 并行运行

c. 分阶段迁移

设计适配层和集成机制

制定迁移计划和里程碑

识别风险和缓解措施

预期结果:

  • 迁移方案设计文档
  • 服务边界定义
  • 迁移路线图
  • 风险评估报告

思考题:

迁移过程中如何保证系统可用性?

如何处理数据迁移和一致性?

团队需要哪些新技能?如何准备?


七、本章小结

架构风格是系统的高层组织模式,决定了系统的整体结构和组件交互方式。

核心要点:

常见架构风格:

- 分层架构:清晰的层次划分,适合企业系统

- 客户端-服务器:集中式服务,适合 Web 应用

- 管道-过滤器:数据流处理,适合批处理

- 事件驱动:松耦合异步,适合实时系统

- 微服务:独立部署,适合大型复杂系统

风格的选择:

- 没有最好的风格,只有最适合的风格

- 考虑业务需求、团队能力、技术约束

- 可以组合多种风格

风格的约束:

- 每种风格都有明确的结构约束

- 违反约束会失去风格的好处

- 深入理解约束是关键

风格的演化:

- 风格可以随系统演进而变化

- 渐进式迁移优于大爆炸式重写

- 保持向后兼容

风格选择指南:

系统特征推荐风格
小型简单系统单体分层
Web 应用客户端-服务器 + 分层
数据处理管道-过滤器
实时系统事件驱动
大型复杂系统微服务
突发负载Serverless

八、习题

基础题

解释架构风格的定义,列出构成风格的四个要素。

比较分层架构和微服务架构的优缺点。

管道-过滤器风格为什么不适合交互式应用?

事件驱动架构的核心优势和挑战分别是什么?

列举三种可以组合使用的架构风格,并说明组合方式。

进阶题

为一个在线教育平台设计架构风格:

a) 系统包含视频播放、作业提交、在线考试、讨论区等功能

b) 预期用户 10 万,并发 5000

c) 需要支持快速迭代和新功能上线

说明选择的风格和理由。

分析 MVC、MVP、MVVM 三种 UI 架构风格的区别和适用场景。

设计一个从单体架构迁移到微服务架构的策略,包括:

a) 服务拆分原则

b) 迁移步骤

c) 风险控制

d) 验证方法

挑战题

研究事件溯源(Event Sourcing)风格:

a) 事件溯源的核心原理是什么?

b) 与传统 CRUD 相比有什么优势和劣势?

c) 用一个具体案例演示事件溯源的实现

d) 事件溯源如何与 CQRS 结合?

设计一个新的架构风格,用于支持 AI 驱动的实时决策系统:

a) 定义风格的组件和连接件

b) 说明风格的约束和语义

c) 分析风格的适用场景

d) 用一个案例演示风格的应用

5/10架构风格
0%