第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 应用程序
- 需要清晰职责分离的系统
- 团队按技能分工的组织
变体
- 严格分层:只能调用直接下层
- 宽松分层:可以调用任何下层
- 开放分层:允许跨层调用(但有控制)
代码示例
// 表现层
@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),在过滤器之间传输数据
- 约束:
- 过滤器应该是无状态的
- 过滤器之间不直接通信,只通过管道
- 数据流是单向的
直觉解释
通俗地说,就像工厂的流水线。原材料进入流水线,经过一道道工序加工,每一步只做一件事,最后产出成品。每道工序不知道前后工序的细节,只关心自己的输入和输出。
典型应用
- 编译器:词法分析 → 语法分析 → 语义分析 → 代码生成
- 图像处理:读取 → 滤波 → 增强 → 压缩 → 保存
- 日志处理:收集 → 过滤 → 聚合 → 分析 → 存储
优点
- 高内聚低耦合:每个过滤器职责单一
- 可复用:过滤器可以组合使用
- 可并行:过滤器可以并行执行
- 易于理解:数据流清晰可见
缺点
- 不适合交互:难以处理用户交互
- 数据传输开销:数据需要在过滤器间复制
- 错误处理复杂:某个过滤器失败影响整个管道
- 共享状态困难:过滤器无状态限制了设计
适用场景
- 批处理任务
- 数据转换和处理
- 信号处理
- 编译器和分析器
代码示例
// 过滤器接口
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(命令查询责任分离)
- 写操作(命令)和读操作(查询)分离
- 写侧生成事件,读侧消费事件更新视图
优点
- 松耦合:生产者和消费者互不依赖
- 可扩展性:可以轻松添加新的消费者
- 弹性:消费者可以异步处理,缓冲峰值负载
- 审计性:事件日志提供完整的操作历史
缺点
- 复杂性:异步处理增加调试难度
- 一致性:最终一致性可能导致临时不一致
- 顺序保证:确保事件顺序需要额外机制
- 重复处理:需要幂等性设计
适用场景
- 实时数据处理
- 微服务间通信
- 用户活动追踪
- 工作流和业务流程
代码示例
// 事件定义
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 展示一个完整的分层架构示例。
/**
* 电商系统分层架构示例
*
* 层次:
* 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 展示事件驱动架构的实现。
"""
事件驱动架构示例
在线书店的订单处理流程
"""
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) 用一个案例演示风格的应用