设计模式与架构设计:分层架构中的外观、微服务中的代理、事件驱动中的观察者
说实话,很多朋友学设计模式时,总觉得这是“玩具代码”。
一个模式一个例子,背得滚瓜烂熟,一到真实项目就懵了。
为什么?因为模式不是孤立存在的。它们要跟架构结合,才能发挥真正威力。
今天我就拿三个最常见的架构场景,聊聊外观、代理、观察者这三个模式是怎么“实战”的。
一、分层架构中的外观模式(Facade)
先说说分层架构。这是最经典的架构之一,Controller、Service、DAO 三层。
但有个问题:Controller 层如果直接调用多个 Service,代码会变得很“碎”。
比如用户注册,要调用户 Service、积分 Service、通知 Service……
Controller 里写一堆调用,耦合高,还不好测。
我在项目中遇到过类似场景。当时有个订单模块,前端需要同时查订单、查物流、查优惠券。一开始 Controller 里写了三个 Service 调用,后来需求一变,改得我头皮发麻。
后来我加了一个 OrderFacade,把这三个查询封装成一个方法。前端只调一次,后端内部随便改,前端不用动。
// Java 示例:外观模式在分层架构中的应用
public class OrderFacade {
private OrderService orderService;
private LogisticsService logisticsService;
private CouponService couponService;
public OrderDetailVO getOrderDetail(String orderId) {
Order order = orderService.getById(orderId);
Logistics logistics = logisticsService.queryByOrderId(orderId);
List<Coupon> coupons = couponService.getAvailableCoupons(order.getUserId());
return new OrderDetailVO(order, logistics, coupons);
}
}
说白了,外观模式就是给复杂子系统“包一层皮”。
Controller 只跟 Facade 打交道,不用关心内部有多少 Service。
二、微服务中的代理模式(Proxy)
微服务架构里,服务之间要远程调用。直接写 HTTP 请求?太原始了。
我们通常用 Feign、gRPC 这类 RPC 框架。它们本质上就是代理模式。
代理模式,说白了就是“替身”。客户端不直接访问真实对象,而是通过代理来访问。
代理可以在调用前后加一些额外逻辑,比如负载均衡、熔断、日志、鉴权。
// Java 示例:微服务中的代理模式(Feign 客户端)
@FeignClient(name = "user-service", url = "http://user-service:8080")
public interface UserServiceProxy {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Long id);
}
// 调用方只需要注入这个代理接口
@Service
public class OrderService {
@Autowired
private UserServiceProxy userServiceProxy;
public void createOrder(Long userId) {
User user = userServiceProxy.getUserById(userId);
// 业务逻辑...
}
}
你看,调用方根本不知道背后是 HTTP 调用还是本地调用。代理把远程调用的复杂性隐藏了。
代理模式在微服务里还有一个常见应用:API 网关。网关就是所有微服务的统一代理入口,负责路由、限流、认证。
三、事件驱动中的观察者模式(Observer)
事件驱动架构,现在越来越流行。比如用户下单后,要发短信、发邮件、更新库存、记录日志。
如果把这些逻辑都写在订单 Service 里,代码会变得又长又臭。
观察者模式就是解决这个问题的:一个事件发生,多个监听者自动响应。
// Java 示例:观察者模式在事件驱动中的应用
// 事件
public class OrderCreatedEvent {
private Long orderId;
private Long userId;
// getter/setter...
}
// 事件发布者
@Component
public class EventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void publishOrderCreated(OrderCreatedEvent event) {
publisher.publishEvent(event);
}
}
// 观察者(监听者)
@Component
public class SmsListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("发送短信给用户:" + event.getUserId());
}
}
@Component
public class InventoryListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("扣减库存,订单ID:" + event.getOrderId());
}
}
Spring 的 @EventListener 就是观察者模式的典型实现。发布者只管发事件,监听者各自处理自己的逻辑。
这样做的好处很明显:
- 解耦: 订单 Service 不用知道谁在监听
- 可扩展: 加一个新监听者,不用改现有代码
- 异步: 可以配合线程池或消息队列,实现异步处理
四、三种模式的关系与对比
这三种模式,其实代表了三种不同的架构思想:
| 模式 | 架构场景 | 核心作用 | 我常用的场景 |
|---|---|---|---|
| 外观模式 | 分层架构 | 简化调用,统一入口 | Controller 层封装多个 Service 调用 |
| 代理模式 | 微服务架构 | 远程调用,增强控制 | Feign 客户端、API 网关 |
| 观察者模式 | 事件驱动架构 | 解耦,异步响应 | 订单事件、通知推送 |
你想想看,这三种模式其实都在做同一件事:分离关注点。
外观模式把调用方和子系统分离;代理模式把客户端和真实服务分离;观察者模式把事件发布者和监听者分离。
架构设计的本质,不就是把复杂的东西拆开,再通过模式把它们优雅地连起来吗?
五、一张图看懂三种模式在架构中的位置
下面这张 SVG 图,展示了三种模式在典型架构中的位置和作用。我画的时候特意把层次分清楚,方便你对照理解。
嗯,这张图把三种模式在架构中的位置和关系画清楚了。你可以保存下来,以后做架构设计时对照着看。
六、避坑指南与个人经验
最后,分享几个我这些年积累的经验:
- 外观模式别滥用: 不是所有 Service 调用都要封装。如果只是简单的一对一调用,加 Facade 反而多余。我一般等 Controller 里出现三个以上 Service 调用时,才考虑加 Facade。
- 代理模式注意性能: 远程代理毕竟有网络开销。别在循环里调代理接口,能批量查询就批量查。我曾经见过一个同事在 for 循环里调 Feign 接口,结果接口响应从 200ms 变成 20s。
- 观察者模式注意异常处理: 一个监听者抛异常,会不会影响其他监听者?Spring 默认是同步的,一个挂了,后面的都不执行。我建议要么用异步,要么在监听者内部 try-catch。
好了,这一章就聊到这儿。模式是死的,架构是活的。多在实际项目中琢磨,你自然就知道什么时候该用哪个模式了。