架构演进之路:从单体到微服务,再到事件驱动

说实话,我入行那会儿,大家做系统都是清一色的单体架构。一个 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 下层 核心逻辑纯单元测试
技术切换 影响面大 只改适配器
学习曲线 中等
避坑指南: 我曾经在一个项目里强行用六边形架构,结果团队里新人看不懂,代码越写越乱。架构选型要因地制宜,别为了用而用。

五、架构演进的核心逻辑

说了这么多,其实架构演进就一个核心逻辑:解耦

  • 单体 -> 微服务:按业务解耦
  • 同步 -> 事件驱动:按时间解耦
  • 分层 -> 六边形:按依赖方向解耦

每次解耦,都会带来新的复杂度。微服务带来了服务治理的复杂度,事件驱动带来了最终一致性的复杂度,六边形架构带来了代码组织的复杂度。

嗯,这就是架构师的日常——在解耦和复杂度之间找平衡。

我的建议: 别一上来就上微服务、事件驱动。先问自己三个问题:业务真的需要吗?团队能驾驭吗?运维跟得上吗?如果答案是否定的,老老实实用单体,把代码写干净,比什么都强。
架构演进核心逻辑 单体架构 按业务拆 微服务架构 按时间拆 事件驱动 分层架构 按依赖拆 六边形架构 核心逻辑:解耦 → 带来新复杂度 → 寻找平衡
最后说一句: 架构没有银弹。我见过用单体架构跑得飞快的系统,也见过微服务搞得一团糟的团队。关键是人,不是技术。把基础打牢,比追新概念重要得多。

专注资料整理