• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
编写人生
写写代码,写写人生
博客园    首页    新随笔    联系   管理    订阅  订阅

软件架构设计模式全览

软件架构设计模式全览

第一章:引言

软件架构是一个系统的高层结构,它定义了系统的组织方式、组件之间的关系以及指导设计和演化的原则。好的架构能够让系统易于理解、易于修改、易于测试,并且能够满足功能性和非功能性需求。

本文将系统性地介绍常见的软件架构设计模式,包括分层架构、微服务架构、事件驱动架构、CQRS、领域驱动设计等,并结合实际案例进行分析。


第二章:分层架构(Layered Architecture)

2.1 概述

分层架构是最经典、最广泛使用的架构模式之一。它将系统划分为若干层,每一层只与相邻层交互,形成清晰的职责边界。

典型的四层架构:

  1. 表现层(Presentation Layer):负责用户界面和用户交互
  2. 业务逻辑层(Business Logic Layer):负责核心业务规则
  3. 数据访问层(Data Access Layer):负责数据的读写操作
  4. 数据库层(Database Layer):负责数据的持久化存储

2.2 优点

  • 关注点分离,每层职责明确
  • 易于理解和维护
  • 层与层之间可以独立替换
  • 便于团队分工协作

2.3 缺点

  • 可能出现"污水池"反模式,请求只是简单地穿透各层而不做任何处理
  • 性能开销:每次请求都需要经过多层处理
  • 在大型系统中,层内可能变得臃肿

2.4 适用场景

  • 传统企业应用
  • 内部管理系统
  • 对性能要求不极端的业务系统
# 分层架构示例
class UserRepository:
    """数据访问层"""
    def find_by_id(self, user_id: int):
        return db.query(f"SELECT * FROM users WHERE id = {user_id}")

class UserService:
    """业务逻辑层"""
    def __init__(self, repo: UserRepository):
        self.repo = repo

    def get_user_profile(self, user_id: int):
        user = self.repo.find_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        return {"id": user.id, "name": user.name, "email": user.email}

class UserController:
    """表现层"""
    def __init__(self, service: UserService):
        self.service = service

    def handle_get_user(self, request):
        user_id = request.params.get("id")
        return self.service.get_user_profile(user_id)

第三章:微服务架构(Microservices Architecture)

3.1 概述

微服务架构将一个大型应用拆分为一组小型、独立部署的服务。每个服务运行在自己的进程中,通过轻量级机制(通常是 HTTP API 或消息队列)进行通信。

3.2 核心原则

  • 单一职责:每个服务只负责一个业务能力
  • 独立部署:每个服务可以独立构建、测试和部署
  • 去中心化:数据管理和治理去中心化
  • 容错设计:服务之间的通信需要考虑失败情况

3.3 服务拆分策略

按业务能力拆分(Business Capability)

这是最推荐的拆分方式,按照业务领域来划分服务边界:

  • 用户服务(User Service)
  • 订单服务(Order Service)
  • 支付服务(Payment Service)
  • 库存服务(Inventory Service)
  • 通知服务(Notification Service)

按子域拆分(Subdomain)

结合领域驱动设计,识别核心域、支撑域和通用域,分别建立对应的微服务。

3.4 通信模式

同步通信:

  • REST over HTTP/HTTPS
  • gRPC(高性能场景)
  • GraphQL(灵活查询场景)

异步通信:

  • 消息队列(RabbitMQ、Kafka)
  • 事件总线
  • 发布/订阅模式

3.5 挑战与解决方案

挑战 解决方案
分布式事务 Saga 模式、两阶段提交
服务发现 Consul、Eureka、Kubernetes DNS
负载均衡 Nginx、Envoy、Istio
熔断降级 Hystrix、Resilience4j
链路追踪 Jaeger、Zipkin、SkyWalking
配置管理 Apollo、Nacos、Consul

第四章:事件驱动架构(Event-Driven Architecture)

4.1 概述

事件驱动架构(EDA)是一种以事件为核心的架构模式。系统中的组件通过发布和订阅事件来进行通信,生产者和消费者之间完全解耦。

4.2 核心概念

事件(Event)

事件是系统中发生的某件事情的记录,通常是不可变的。例如:

  • OrderPlaced(订单已下单)
  • PaymentCompleted(支付已完成)
  • UserRegistered(用户已注册)

事件生产者(Event Producer)

负责产生事件并发布到事件总线或消息队列。

事件消费者(Event Consumer)

订阅感兴趣的事件并进行处理。一个事件可以有多个消费者。

事件总线(Event Bus)

负责事件的路由和分发,常见实现有 Kafka、RabbitMQ、AWS EventBridge 等。

4.3 事件溯源(Event Sourcing)

事件溯源是一种特殊的数据存储方式,不存储当前状态,而是存储所有导致当前状态的事件序列。

优点:

  • 完整的审计日志
  • 可以重放事件重建任意时间点的状态
  • 天然支持时间旅行调试

缺点:

  • 查询当前状态需要重放所有事件(可用快照优化)
  • 事件 schema 变更复杂
  • 学习曲线较高
# 事件溯源示例
from dataclasses import dataclass
from typing import List
from datetime import datetime

@dataclass
class Event:
    event_type: str
    timestamp: datetime
    data: dict

class BankAccount:
    def __init__(self, account_id: str):
        self.account_id = account_id
        self.balance = 0
        self.events: List[Event] = []

    def deposit(self, amount: float):
        event = Event(
            event_type="MoneyDeposited",
            timestamp=datetime.now(),
            data={"amount": amount}
        )
        self._apply(event)
        self.events.append(event)

    def withdraw(self, amount: float):
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        event = Event(
            event_type="MoneyWithdrawn",
            timestamp=datetime.now(),
            data={"amount": amount}
        )
        self._apply(event)
        self.events.append(event)

    def _apply(self, event: Event):
        if event.event_type == "MoneyDeposited":
            self.balance += event.data["amount"]
        elif event.event_type == "MoneyWithdrawn":
            self.balance -= event.data["amount"]

    @classmethod
    def rebuild_from_events(cls, account_id: str, events: List[Event]):
        account = cls(account_id)
        for event in events:
            account._apply(event)
        return account

4.4 CQRS(命令查询职责分离)

CQRS 将系统的读操作(Query)和写操作(Command)分离到不同的模型中。

命令端(Write Side)

  • 处理业务命令
  • 维护写模型(通常是领域模型)
  • 发布领域事件

查询端(Read Side)

  • 处理查询请求
  • 维护读模型(通常是针对查询优化的扁平化视图)
  • 订阅领域事件,更新读模型
用户请求
    │
    ├── 写操作 ──→ Command Handler ──→ Domain Model ──→ Event Store
    │                                                        │
    │                                                        ↓
    └── 读操作 ──→ Query Handler ──→ Read Model ←── Event Handler

第五章:领域驱动设计(Domain-Driven Design)

5.1 战略设计

限界上下文(Bounded Context)

限界上下文是 DDD 中最重要的概念之一。它定义了一个模型的适用边界,在这个边界内,所有的术语、概念和规则都有明确且一致的含义。

不同限界上下文之间通过上下文映射(Context Map)来描述关系:

  • 共享内核(Shared Kernel):两个上下文共享部分模型
  • 客户/供应商(Customer/Supplier):上游提供服务,下游消费
  • 防腐层(Anti-Corruption Layer):隔离外部系统的影响
  • 开放主机服务(Open Host Service):提供标准化的集成接口
  • 发布语言(Published Language):使用共享的语言进行集成

通用语言(Ubiquitous Language)

在限界上下文内,开发人员和领域专家使用同一套语言来描述业务概念。这套语言应该体现在代码中,避免"翻译"带来的信息损失。

5.2 战术设计

实体(Entity)

具有唯一标识的对象,其标识在整个生命周期内保持不变。

class Order:
    def __init__(self, order_id: str, customer_id: str):
        self.order_id = order_id  # 唯一标识
        self.customer_id = customer_id
        self.items = []
        self.status = "PENDING"

    def add_item(self, product_id: str, quantity: int, price: float):
        self.items.append({
            "product_id": product_id,
            "quantity": quantity,
            "price": price
        })

    def confirm(self):
        if not self.items:
            raise ValueError("Cannot confirm empty order")
        self.status = "CONFIRMED"

值对象(Value Object)

没有唯一标识,通过属性值来判断相等性的对象。值对象应该是不可变的。

from dataclasses import dataclass

@dataclass(frozen=True)
class Money:
    amount: float
    currency: str

    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Cannot add different currencies")
        return Money(self.amount + other.amount, self.currency)

    def multiply(self, factor: float) -> 'Money':
        return Money(self.amount * factor, self.currency)

聚合(Aggregate)

聚合是一组相关对象的集群,作为数据修改的单元。每个聚合有一个根实体(Aggregate Root),外部只能通过根实体访问聚合内的对象。

领域服务(Domain Service)

当某个业务逻辑不自然地属于任何实体或值对象时,可以将其放入领域服务中。

领域事件(Domain Event)

表示领域中发生的重要事情,用于在聚合之间传递信息,保持松耦合。


第六章:六边形架构(Hexagonal Architecture)

6.1 概述

六边形架构,也称为端口与适配器架构(Ports and Adapters),由 Alistair Cockburn 提出。其核心思想是将应用程序的业务逻辑与外部系统(数据库、UI、消息队列等)完全隔离。

6.2 核心概念

应用核心(Application Core)

包含业务逻辑,完全不依赖任何外部框架或基础设施。

端口(Port)

定义应用核心与外部世界交互的接口(抽象)。分为:

  • 驱动端口(Driving Port):外部调用应用核心的接口
  • 被驱动端口(Driven Port):应用核心调用外部系统的接口

适配器(Adapter)

实现端口接口,负责技术细节的转换。例如:

  • HTTP 适配器:将 HTTP 请求转换为应用命令
  • 数据库适配器:实现仓储接口,与具体数据库交互
  • 消息队列适配器:实现消息发布接口
外部世界
  │
  ├── HTTP 请求 ──→ [HTTP Adapter] ──→ (Driving Port) ──→ Application Core
  ├── CLI 命令  ──→ [CLI Adapter]  ──→ (Driving Port) ──→
  │
  └── Application Core ──→ (Driven Port) ──→ [DB Adapter]    ──→ PostgreSQL
                                          ──→ [Cache Adapter] ──→ Redis
                                          ──→ [MQ Adapter]    ──→ Kafka

6.3 实现示例

# 端口定义(接口)
from abc import ABC, abstractmethod

class UserRepositoryPort(ABC):
    @abstractmethod
    def save(self, user) -> None: ...

    @abstractmethod
    def find_by_id(self, user_id: str): ...

class EmailServicePort(ABC):
    @abstractmethod
    def send_welcome_email(self, email: str, name: str) -> None: ...

# 应用核心(不依赖任何具体技术)
class RegisterUserUseCase:
    def __init__(
        self,
        user_repo: UserRepositoryPort,
        email_service: EmailServicePort
    ):
        self.user_repo = user_repo
        self.email_service = email_service

    def execute(self, command):
        user = User(id=generate_id(), name=command.name, email=command.email)
        self.user_repo.save(user)
        self.email_service.send_welcome_email(user.email, user.name)
        return user

# 适配器实现
class PostgresUserRepository(UserRepositoryPort):
    def save(self, user) -> None:
        db.execute("INSERT INTO users ...", user)

    def find_by_id(self, user_id: str):
        return db.query("SELECT * FROM users WHERE id = ?", user_id)

class SendGridEmailService(EmailServicePort):
    def send_welcome_email(self, email: str, name: str) -> None:
        sendgrid_client.send(to=email, template="welcome", data={"name": name})

第七章:Serverless 架构

7.1 概述

Serverless 架构让开发者无需管理服务器基础设施,只需关注业务逻辑代码。云提供商负责自动扩缩容、高可用等运维工作。

7.2 核心特征

  • 无服务器管理:开发者不需要配置或管理服务器
  • 按需执行:函数只在被调用时运行
  • 自动扩缩容:根据请求量自动调整
  • 按使用付费:只为实际执行的时间和资源付费

7.3 适用场景

  • 事件驱动的数据处理(文件上传触发处理)
  • API 后端(低频或突发流量)
  • 定时任务
  • 实时流处理
  • 聊天机器人和 AI 推理

7.4 挑战

  • 冷启动延迟:函数首次调用时需要初始化
  • 执行时间限制:通常有最大执行时间限制
  • 调试困难:分布式追踪和本地调试复杂
  • 供应商锁定:不同云厂商的 API 差异较大
  • 状态管理:函数本身是无状态的,需要外部存储

第八章:架构决策记录(ADR)

8.1 什么是 ADR

架构决策记录(Architecture Decision Record)是一种轻量级的文档实践,用于记录重要的架构决策、决策背景、考虑过的方案以及最终选择的理由。

8.2 ADR 模板

# ADR-001: 选择消息队列技术

## 状态
已接受

## 背景
系统需要在订单服务和库存服务之间进行异步通信,
需要选择合适的消息队列技术。

## 决策
选择 Apache Kafka 作为消息队列。

## 考虑的方案

### 方案一:RabbitMQ
- 优点:成熟稳定,支持多种消息协议,管理界面友好
- 缺点:消息不持久化(默认),吞吐量相对较低

### 方案二:Apache Kafka
- 优点:高吞吐量,消息持久化,支持消息回放,生态丰富
- 缺点:运维复杂,学习曲线较高

### 方案三:AWS SQS
- 优点:全托管,无运维负担
- 缺点:供应商锁定,功能相对简单

## 结果
选择 Kafka,因为系统对吞吐量要求高,且需要消息回放能力
用于数据分析和故障恢复。

## 后果
- 需要团队学习 Kafka 运维知识
- 需要部署 Zookeeper(或使用 KRaft 模式)
- 获得了高吞吐量和消息持久化能力

第九章:架构评估方法

9.1 ATAM(架构权衡分析方法)

ATAM 是一种系统化的架构评估方法,通过识别架构决策与质量属性之间的权衡关系来评估架构。

主要步骤:

  1. 收集架构驱动因素(业务目标、质量属性场景)
  2. 描述架构
  3. 识别架构方法
  4. 生成质量属性效用树
  5. 分析架构方法
  6. 识别敏感点和权衡点
  7. 头脑风暴场景
  8. 分析结果

9.2 质量属性

在架构评估中,常见的质量属性包括:

质量属性 描述 常见度量指标
性能 系统响应时间和吞吐量 响应时间 P99、TPS
可用性 系统正常运行的时间比例 SLA(如 99.9%)
可扩展性 系统处理增长负载的能力 水平/垂直扩展能力
安全性 系统抵御攻击的能力 漏洞数量、合规性
可维护性 系统修改和演化的难易程度 代码复杂度、测试覆盖率
可测试性 系统验证行为的难易程度 单元测试覆盖率
可部署性 系统部署的频率和风险 部署频率、MTTR

第十章:总结与选型建议

10.1 架构模式对比

架构模式 复杂度 适用规模 主要优势 主要挑战
分层架构 低 小到中型 简单易懂 可能变得臃肿
微服务 高 中到大型 独立部署、技术多样性 分布式复杂性
事件驱动 中到高 中到大型 松耦合、高扩展性 最终一致性
六边形架构 中 任意规模 业务逻辑隔离 初期设计成本
Serverless 中 小到中型 无运维、弹性 冷启动、调试

10.2 选型建议

初创团队 / 小型项目

从单体架构或分层架构开始,不要过早引入微服务。等到团队规模增长、业务复杂度提升后,再考虑拆分。

中型团队 / 成长期产品

可以考虑模块化单体(Modular Monolith)作为过渡,保持代码内部的模块边界清晰,为未来拆分做准备。

大型团队 / 复杂业务

微服务架构配合 DDD 是较好的选择。但要注意:

  • 服务粒度不要过细
  • 建立完善的 DevOps 基础设施
  • 重视服务治理和可观测性

事件密集型系统

事件驱动架构非常适合需要处理大量异步事件的系统,如 IoT 数据处理、实时分析、通知系统等。

10.3 架构演进原则

  1. 从简单开始,按需演进
  2. 保持架构决策的可逆性
  3. 记录重要的架构决策(ADR)
  4. 定期进行架构评审
  5. 关注团队认知负荷,不要引入超出团队能力的复杂度
  6. 架构服务于业务,而不是为了技术而技术

本文档涵盖了主流软件架构模式的核心概念、优缺点和适用场景,供架构设计参考使用。

posted on 2026-04-09 14:08  编写人生  阅读(70)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3