在今天的软件开发实践中,“架构”已经不只是技术选型问题,而是关乎团队协作效率、系统可演进性以及业务竞争力的核心决策。很多初学者接触架构时,往往只看到零散的名词:分层、MVC、微服务、事件驱动、CQRS、Serverless……却很难把它们串成一条清晰的演进主线,更难落到实际项目中“该怎么选、怎么用”。
下面我会按照从传统到现代、从单体到云原生、从结构到哲学的脉络,系统讲解开发中常用的几类架构:它们各自解决什么问题、内部结构是什么样、在现代实践中如何组合使用,以及在不同项目阶段应如何做架构决策。
一、从单体分层到“整洁单体”:传统架构的现代价值
1. 经典分层架构:从“堆代码”到“有序单体”
长期以来,企业应用最普遍的形态是单体+分层架构:整个系统打成一个包部署,但内部按职责划分为几层:
- 表现层(Controller / View):处理 HTTP 请求、页面渲染、API 返回
- 业务层(Service):封装领域规则和业务流程
- 数据访问层(DAO / Repository):与数据库、缓存打交道
- 数据库层:MySQL、PostgreSQL、MongoDB 等
这种结构的本质是:利用“纵向分层”让职责清晰、依赖有序。即便到今天,它依然是绝大多数中小项目最合适的起点。
现代实践中的演进:
过去的分层单体容易演变成“巨石应用”:所有业务都堆在一个 Service 包里,Service 调 Service 混乱不堪。为了解决这个问题,业界开始引入两种思路:
垂直切片(Vertical Slice):
不再只关注“横向分层”,而是在单体中按业务领域做“纵向切割”:/order ├─ OrderController ├─ OrderService ├─ OrderRepository /payment ├─ PaymentController ├─ PaymentService └─ PaymentRepository每个业务域形成独立模块,内部依然保持分层,但模块间依赖受控。这让单体内部的组织更加接近微服务,却保留了单体的部署和运维简单性。
整洁架构思路的融入(Clean Architecture):
Clean Architecture 强调:业务逻辑不应依赖框架与基础设施。应用被组织成同心圆:- 最内层:实体 / 领域模型(Entities)
- 中层:用例 / 应用服务(Use Cases / Application Services)
- 外层:接口适配器(Controllers、Presenters、Gateways)
- 最外层:框架与驱动(Web 框架、DB 驱动、消息中间件等)
所有依赖只能“由外向内”。这保证了:
- 核心业务代码可以在不启动 Web 容器、不连接数据库的情况下被测试
- 更换 Web 框架、数据库或消息中间件时,对内核影响有限
在实战中,一个做得好的“整洁单体”,往往比一个拆得很随意的“微服务集群”更稳定、更经济。
二、界面层架构:从 MVC 到组件化与 BFF
1. MVC / MVVM:界面内部的组织方式
MVC(Model-View-Controller)最初作为 UI 架构模式,引导我们把界面展示逻辑与业务处理解耦:
- Model:数据模型和基本业务规则
- View:界面与展示
- Controller:协调用户输入、业务调用和视图选择
在后端框架中,如 Spring MVC、Django、ASP.NET MVC,MVC 更像是“请求-响应层”的组织方式。
进入前端 SPA(单页应用)时代,主流演变为:
- MVVM(Vue、Knockout):通过双向绑定,View 与 ViewModel 之间数据自动同步
- 组件化 + 单向数据流(React):一切皆组件,状态自上而下传递,事件自下而上传递
这些都是界面层内部的架构模式,通常会与后端的分层架构、微服务架构结合使用。
2. BFF(Backend For Frontend):多端时代的“前后端中间层”
当一个系统同时服务 Web、iOS、Android、小程序、多种第三方系统时,“一个后端供所有前端使用”容易带来两个问题:
- API 难以同时满足多个前端对数据形态和性能的不同诉求
- 后端为适配多个前端而膨胀,接口逻辑变得非常复杂
BFF 模式的解决方式是:
为每类前端定制一个轻量后端层,由它来负责:
- 聚合多个微服务的数据,为前端组装所需视图模型
- 处理各终端差异化需求(字段裁剪、分页策略、缓存策略等)
- 屏蔽后端服务拓扑变化,降低前端耦合度
架构上常见组合是:
“微服务集群” + “API 网关” + “多个 BFF 应用(按端划分)”。
三、C/S 与 B/S:从部署形态到边缘计算
传统的 C/S(客户端–服务器)与 B/S(浏览器–服务器)之分,本质上是**“计算与状态位于何处”**的问题:
- C/S:厚客户端,许多逻辑在本地运行;更新成本高,但可离线、可深度优化体验
- B/S:浏览器作为统一运行时;部署更新集中在服务器侧;依赖网络质量
近年来边缘计算、PWA(渐进式 Web 应用)兴起后,这两者的界限变得模糊:
- Web 应用通过 Service Worker 实现离线缓存、本地存储与后台同步,获得类 C/S 的体验
- 传统桌面应用也大量采用 Web 技术(如 Electron),本质是“一个内嵌浏览器 + 本地资源”,与 B/S 融合
现代项目通常不再单纯讨论“选 C/S 还是 B/S”,而是综合考虑:
- 用户是否需要离线访问、大量本地计算或硬件能力
- 是否有严格的运维、升级和安全控制要求
- 是否需要通过边缘节点(CDN、Edge Function)进行前置计算
四、事件驱动架构(EDA):向解耦与实时演进
当系统规模增大、业务流程变复杂,一个业务动作往往需要衍生出多种“后续行为”,例如:
- 用户下单 → 扣库存 → 发送短信通知 → 推送推荐 → 更新统计报表
- 用户注册 → 发送欢迎邮件 → 创建新手任务 → 发放优惠券
用同步调用去串联这些步骤,会让服务间依赖链条冗长、可靠性变差、耦合度高。
事件驱动架构通过“发布-订阅”的方式解耦这一切:
- 核心服务完成主业务后,发布领域事件(如
OrderCreated) - 其他服务订阅这些事件,根据需要做“后续操作”
底层通常采用消息队列/流处理平台(如 Kafka、RabbitMQ、Pulsar),整体模式带来:
- 高解耦:新增一个“收到订单就发短信”的需求,只需新增一个消费者,不动原有代码
- 异步与削峰填谷:高峰时事件排队处理,平滑后端压力
- 更自然地支撑实时分析与风控:基于事件流做实时计算
但 EDA 也引入复杂性:
- 一致性和顺序性:同一订单相关的多个事件处理顺序、幂等性问题需要设计
- 可观测性:调用链不再是简单的“函数栈”,需要链路追踪和日志关联
- 团队心智模型:从“过程式思维”过渡到“事件驱动思维”有一定门槛
五、CQRS 与事件溯源:读写分离背后的架构深意
当系统的读写负载、性能要求和业务复杂度不断提升时,“一张表同时满足读写”会遇到瓶颈:
- 写:需要严格的业务规则验证、一致性保障、事务处理
- 读:需要灵活多变的视图、复杂的查询条件、极致的性能
**CQRS(命令查询职责分离)**就是在架构层面对这种矛盾的回应:
- 写侧(Command Side):专注处理业务命令,维护“事实”,强调完整性和一致性
- 读侧(Query Side):专注为界面/接口提供高性能查询,数据结构可以为“读”而生
在实践中,CQRS 往往与**事件溯源(Event Sourcing)**结合,形成一种强力模式:
- 状态不再直接存为“最新快照”,而是存为“事件序列”:
例如订单状态不存为一个status字段,而是OrderCreated、OrderPaid、OrderShipped等事件的有序列表。 - 当前状态可通过重放事件得到:
- 写侧只需保障事件的正确写入
- 读侧从事件流构建各类“物化视图”(Materialized Views)
优点包括:
- 对读写模型进行独立优化和横向扩展
- 任意重建查询视图以适应新需求,支持“回放历史”、审计和调试
- 对读侧采用高度去范式化的表结构,查询效率极高
代价则是:
- 必须接受最终一致性:读侧数据可能比写侧略滞后
- 实现与运营复杂度提高,需要团队在 DDD、消息处理、幂等等方面具备成熟经验
- 并非适合所有业务,尤其不适合简单 CRUD 场景
六、微服务架构:从“大拆分”到精细演进
微服务不应该被看成一种“潮流”,而是在业务与团队规模发展到一定程度后,为解决单体架构扩展瓶颈的自然结果。
1. 微服务的核心原则
- 按业务能力拆分服务,而不是按技术层次拆分(比如“订单服务”“库存服务”,而不是“公共 Service1、Service2”)
- 每个服务独立部署、独立扩缩容、独立技术栈甚至独立数据库
- 服务间通过明确定义的 API 或消息进行通信(HTTP/gRPC/消息队列)
围绕微服务,现代体系中几乎总会出现的一整套基础设施:
- API 网关:统一入口,路由、认证、限流、灰度发布
- 服务注册与发现:如 Nacos、Consul,让服务知道“别人在哪儿”
- 配置中心:集中管理配置和密钥
- 服务网格(Service Mesh):如 Istio/Linkerd,将流量治理、熔断、重试、指标采集下沉到“基础设施层”
- 可观测性体系:统一日志、指标、链路追踪
2. 微服务何时“值得上”
一般可以从以下几个维度判断:
- 业务复杂度:领域多、边界清晰,如大型电商、金融交易、ERP 平台
- 团队规模:多团队、多职能协作,需要各自独立迭代和部署
- 非功能性需求:高可用、高并发、可弹性扩容、强自治
同时也必须看到其成本:
- 分布式带来的一致性、事务、调试难题
- 运维与监控复杂度显著上升,对 DevOps/平台工程能力要求极高
- 业务划分不当会导致“分布式单体”,既失去了单体的简单,又没有获得真正的解耦
实战更推荐的路径是:
先把单体做干净 —— 按领域模块化、引入整洁架构思想、完善自动化测试和 CI/CD —— 等单体在组织或性能层面的瓶颈真正显现,再有计划地“按业务域逐步拆分为微服务”。
七、Serverless:无服务器背后的新范式
Serverless(无服务器架构)并非不需要服务器,而是将服务器的管理责任完全交给云厂商,开发者只关注函数或小服务级别的业务逻辑:
- FaaS(函数即服务):如 AWS Lambda、阿里云函数计算
- Serverless 容器:如 Cloud Run、Fargate
关键特征:
- 按调用量和执行时间计费,极大适合低频或波动流量场景
- 自动伸缩,无需自己搭建和维护集群
- 推荐用于:
- 事件驱动型任务(文件上传、消息消费、定时任务)
- 一些边缘逻辑(如活动页接口、Webhook 处理器)
- 业务流量峰谷变化很大的场景
但要注意:
- 存在“冷启动”问题,首次调用可能有明显延迟
- 有较强的厂商绑定,迁移成本高
- 架构上必须非常注意状态外置(数据库、缓存、对象存储)
在现代系统中,一个比较理想的组合是:
核心业务用“整洁单体或微服务”承载;大量边缘功能用 Serverless 承接,达成成本、弹性和复杂度的平衡。
八、如何根据项目阶段选择合适架构(可操作建议)
结合上面这些模式,可以给出一个偏“落地”的选型框架。你可以粗略对照自己的项目,看看更适合哪条路线。
1. 初创或中小项目(单团队、需求模糊)
推荐:
- 技术形态:B/S + 分层单体
- 组织方式:按业务领域做模块划分(垂直切片)
- 架构原则:尽量应用 Clean Architecture 思想,确保业务逻辑与框架解耦
重点:
- 不要过早上微服务,不要为尚不存在的复杂性买单
- 用清晰的代码结构、自动化测试和规范的接口设计来“预防未来的腐化”
2. 业务逐渐复杂、团队开始分拆
可以引入:
- 领域驱动设计(DDD)的基础概念:领域、聚合、限界上下文
- 局部采用事件驱动(EDA)优化解耦与异步处理
- 对某几个“读压力巨大”的功能使用 CQRS 做读写分离
此阶段目标:
- 在保持单体部署的前提下,先把“架构内功”练扎实
- 通过清晰领域边界,为未来的微服务拆分打下基础
3. 多团队、多业务线、高并发要求
可以渐进式引入:
- 微服务架构:从最独立、最稳定的领域开始拆分(如用户、账务、通知)
- API 网关、服务注册发现、配置中心、链路追踪等配套设施
- 服务网格用于统一治理调用、流量与安全
同时:
- 对关键业务链路可以叠加事件驱动架构,简化耦合、提升扩展性
- 在交易、风控、报表等复杂场景引入 CQRS + 事件溯源(视业务复杂度选择)
4. 大规模云原生与成本优化阶段
在已经稳定运营的微服务体系之上,再引入:
- Serverless 承接零散、波动大的边缘任务
- 在边缘节点部署部分服务,降低延迟
- 使用基础设施即代码(IaC)、GitOps 等管理大规模环境
九、总结:架构的价值,不在“多高级”,而在“多适合”
如果用一句话来概括上面的内容:
架构不是炫技,而是让业务以最小的心智和运维成本,获得足够的灵活性、性能和可靠性。
因此:
- 不要因为“流行”而上微服务、CQRS、Serverless,而要因为“遇到了它所解决的问题”
- 单体并不低级,一个做好了领域划分与整洁设计的单体,是多数团队最具性价比的选择
- 架构不是一锤子买卖,而是一条演进路径:从整洁单体 → 模块化单体 → 渐进式微服务 → 云原生 & Serverless