架构演进之路:从单体到微服务,再到事件驱动
说实话,我入行那会儿,大家做系统都是清一色的单体架构。一个 war 包丢到 Tomcat 里,完事。后来业务复杂了,用户量上来了,才发现这条路走不通了。今天我就结合自己踩过的坑,聊聊架构是怎么一步步演进的。
一、单体架构:简单但脆弱
单体架构,说白了就是把所有功能都塞进一个进程里。你想想看,用户管理、订单处理、支付结算,全在一个项目里。
优点很明显:
- 开发简单,一个 IDE 就能搞定
- 部署方便,一个包扔上去就行
- 调试容易,本地跑起来就能查问题
但缺点也致命:
- 牵一发动全身,改个支付模块得重新部署整个系统
- 扩展性差,订单模块压力大,你只能把整个系统都扩容
- 技术栈锁定,用了 Java 就不能混 Python
我曾经踩过的坑: 有一次线上订单模块出了个内存泄漏,结果整个交易系统都挂了。用户登录、商品浏览全受影响。那叫一个惨,大半夜被叫起来救火。
二、微服务架构:拆开才是出路
后来我主导了一个交易系统的重构。我建议团队拆成微服务。怎么拆?按业务边界来。
典型的拆分方式:
- 用户服务:注册、登录、权限
- 订单服务:下单、改单、查单
- 支付服务:支付、退款、对账
- 商品服务:商品信息、库存
每个服务独立部署,独立数据库。服务之间通过 REST API 或 gRPC 通信。
// 订单服务调用支付服务
@FeignClient(name = "payment-service")
public interface PaymentClient {
@PostMapping("/api/payment/create")
PaymentResponse createPayment(@RequestBody PaymentRequest request);
}
这样做的好处,我深有体会:
- 每个团队只管自己的服务,互不干扰
- 哪个服务压力大,单独扩容就行
- 可以用不同的技术栈,订单服务用 Java,推荐服务用 Python,没问题
我的个人习惯: 拆分微服务时,先画业务领域图。每个 bounded context 就是一个候选服务。别一开始就拆得太细,否则你会被服务间通信搞疯。
三、事件驱动架构:解耦的终极武器
微服务虽然好,但同步调用多了,系统就变成了分布式单体。你想想看,下单时调支付,支付完调库存,库存完调物流...一个环节挂了,整条链路都卡住。
这时候,事件驱动架构就派上用场了。
核心思想: 服务之间不直接调用,而是通过事件总线发布和订阅事件。
// 订单服务发布事件
eventBus.publish(new OrderCreatedEvent(orderId, userId, amount));
// 支付服务订阅事件
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理支付逻辑
}
我参与过一个高并发交易系统,就是用事件驱动搞定的。订单服务只管发事件,支付、库存、物流各自订阅处理。系统吞吐量直接翻了三倍。
事件驱动的关键组件:
- 事件总线:Kafka、RabbitMQ、RocketMQ
- 事件存储:记录所有事件,方便回溯
- 事件处理器:消费事件,执行业务逻辑
注意: 事件驱动不是银弹。它带来了最终一致性,你得接受数据不是实时同步的。我见过不少团队,为了追求强一致性,硬是把事件驱动用成了同步调用,结果得不偿失。
四、分层架构 vs 六边形架构
聊完宏观架构,咱们看看微观的代码组织方式。
分层架构:
- Controller 层:接收请求
- Service 层:业务逻辑
- DAO 层:数据访问
这种架构很直观,我早期做项目都用它。但问题在于:层与层之间耦合太紧。你换个数据库,得改 DAO 层,有时候 Service 层也得跟着改。
六边形架构(端口适配器模式):
- 核心是业务逻辑,不依赖任何外部系统
- 通过端口(接口)与外部交互
- 适配器实现端口,比如数据库适配器、消息队列适配器
// 核心业务逻辑,不依赖任何框架
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
public Order createOrder(CreateOrderCommand command) {
// 纯业务逻辑
Order order = Order.create(command);
repository.save(order);
return order;
}
}
// 端口定义
public interface OrderRepository {
void save(Order order);
}
// 适配器实现
@Repository
public class JpaOrderRepository implements OrderRepository {
// 使用 JPA 实现
}
我个人更偏爱六边形架构。为什么?因为业务逻辑是纯 Java 代码,不依赖 Spring、不依赖数据库。你想换技术栈?换个适配器就行。
| 对比维度 | 分层架构 | 六边形架构 |
|---|---|---|
| 依赖方向 | 上层依赖下层 | 核心不依赖外部 |
| 测试难度 | 需要 mock 下层 | 核心逻辑纯单元测试 |
| 技术切换 | 影响面大 | 只改适配器 |
| 学习曲线 | 低 | 中等 |
避坑指南: 我曾经在一个项目里强行用六边形架构,结果团队里新人看不懂,代码越写越乱。架构选型要因地制宜,别为了用而用。
五、架构演进的核心逻辑
说了这么多,其实架构演进就一个核心逻辑:解耦。
- 单体 -> 微服务:按业务解耦
- 同步 -> 事件驱动:按时间解耦
- 分层 -> 六边形:按依赖方向解耦
每次解耦,都会带来新的复杂度。微服务带来了服务治理的复杂度,事件驱动带来了最终一致性的复杂度,六边形架构带来了代码组织的复杂度。
嗯,这就是架构师的日常——在解耦和复杂度之间找平衡。
我的建议: 别一上来就上微服务、事件驱动。先问自己三个问题:业务真的需要吗?团队能驾驭吗?运维跟得上吗?如果答案是否定的,老老实实用单体,把代码写干净,比什么都强。
最后说一句: 架构没有银弹。我见过用单体架构跑得飞快的系统,也见过微服务搞得一团糟的团队。关键是人,不是技术。把基础打牢,比追新概念重要得多。