找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3702

积分

0

好友

510

主题
发表于 16 小时前 | 查看: 2| 回复: 0

最近做 Code Review,看到一个聪明的新同事,用策略模式、工厂模式、抽象工厂,写了一个极其复杂的订单处理系统。这个系统本来只需要处理两种支付方式,现在却被设计成了可以“轻松扩展”到20种支付方式的架构。

典型的场景:用户下单,根据支付类型调用不同的支付渠道,代码大概长这样:

// 策略模式接口
interface PaymentStrategy {
    PayResult pay(Order order);
}

// 工厂模式
class PaymentStrategyFactory {
    private Map<String, PaymentStrategy> strategies = new HashMap<>();

    public PaymentStrategyFactory() {
        strategies.put("alipay", new AlipayStrategy());
        strategies.put("wechat", new WechatPayStrategy());
        // ... 理论上可以无限扩展
    }

    public PaymentStrategy getStrategy(String type) {
        return strategies.get(type);
    }
}

// 抽象工厂
interface PaymentFactory {
    Validator createValidator();
    Notifier createNotifier();
    Logger createLogger();
}

// 具体使用
public class OrderService {
    private PaymentStrategyFactory strategyFactory;
    private PaymentFactory paymentFactory;

    public PayResult processOrder(Order order) {
        PaymentStrategy strategy = strategyFactory.getStrategy(order.getPayType());
        PaymentFactory factory = getFactory(order.getPayType());

        factory.createValidator().validate(order);
        PayResult result = strategy.pay(order);
        factory.createNotifier().notify(result);
        factory.createLogger().log(result);

        return result;
    }
}

我把这哥们叫过来,问他:“咱们系统现在就支付宝和微信支付两种,为什么不用简单的if-else,或者用枚举+策略模式简化一下?”

他说:“这样设计扩展性好啊,以后加新支付方式特别方便,代码也显得专业😎。”

这个瞬间,让我想聊聊这个话题:在现代后端开发(尤其是微服务时代)中,我们挂在嘴边的那些经典架构模式,90%都是在过度设计。

先澄清一下:我不反对好的设计思想,比如高内聚低耦合、单一职责。我反对的是,把那些为大型单体应用设计的、沉重的、过度抽象的架构模式,生搬硬套到我们现代的微服务、云原生、Serverless的架构里。很多时候,我们需要的是回归简单的实践,而不是理论的堆砌。如果你也对这个话题有共鸣,欢迎来云栈社区聊聊,那里有更多一线开发者分享他们的实战经验和避坑指南。

我们为什么会陷入架构模式的陷阱?

曾经,我也是《设计模式》、《企业应用架构模式》的忠实读者。热衷于在代码里寻找应用Repository模式、CQRS、六边形架构的机会。

我觉得原因有两个:

1. 为了面试造火箭
架构模式是后端面试的重灾区。面试官喜欢问:“你怎么设计一个高并发的系统?”我们为了应对这种问题,不得不背诵各种架构模式的名字和用途,导致工作中也总想“秀一下肌肉”。

2. 技术虚荣心作祟
我们总觉得,能说出几个架构模式的名字,能在代码里用上DDD、事件溯源、CQRS,就代表自己水平更高。仿佛不说个“领域驱动设计”,不提个“最终一致性”,就显得不够资深。

有哪些水土不服的架构模式?

1. 抽象工厂模式(Abstract Factory)

经典用法:为创建相关或依赖对象提供一个接口,而无需指定具体类。

我的吐槽:在微服务架构里,每个服务应该有自己的数据库和业务逻辑。跨服务的对象创建?你确定不是在造分布式单体?

现代做法:每个微服务维护自己的数据模型和工厂逻辑。如果需要跨服务的数据,用API调用或者事件通信,而不是抽象工厂。

// 别搞这么复杂的抽象工厂了
// 微服务A的代码
@Service
public class OrderService {
    // 直接依赖具体的仓储或客户端
    @Autowired
    private PaymentClient paymentClient;

    // 简单清晰
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order();
        order.setItems(request.getItems());
        order.setTotal(request.getTotal());
        // ... 业务逻辑
        return orderRepository.save(order);
    }
}

2. 复杂的事件溯源(Event Sourcing)

经典用法:不存储对象当前状态,而是存储导致状态变化的所有事件。

我的吐槽:兄弟,咱们就是个电商订单系统,不是银行核心系统!你真的需要完整的历史记录来重建每个对象的状态吗?大部分业务,在数据库里加个 version 字段和审计日志就够了。

现代做法:按需使用。只有真正需要完整审计追踪、法律合规要求的场景,才考虑事件溯源。

// 大部分情况下,这样就够了
@Entity
public class Order {
    @Id
    private Long id;
    private String status;
    private BigDecimal amount;

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    // 简单的状态变更
    public void cancel() {
        this.status = "CANCELLED";
        this.updatedAt = LocalDateTime.now();
    }
}

3. CQRS(命令查询职责分离)的滥用

经典用法:把读写模型分开,用不同数据库甚至不同服务处理。

我的吐槽:90%的业务场景,读写比例是9:1甚至99:1。为了那1%的写操作,你把整个架构搞得这么复杂?读写分离数据库+缓存,通常就够了。

现代做法:从简单开始,真的遇到性能瓶颈再考虑CQRS。

// 开始的时候,简单点
@RestController
public class OrderController {

    // 读:走缓存
    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable Long id) {
        return cacheService.getOrLoad(
            "order:" + id,
            () -> orderRepository.findById(id).orElseThrow()
        );
    }

    // 写:直接写库
    @PostMapping("/orders")
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        Order order = orderService.createOrder(request);
        // 清理缓存
        cacheService.evict("order:" + order.getId());
        return order;
    }
}

4. 过度设计的领域驱动设计(DDD)

经典用法:聚合根、实体、值对象、领域服务、仓储接口、防腐层...

我的吐槽:咱们就是一个用户管理模块,用户有基本信息、地址、权限。真的需要搞出 UserAggregateRootAddressValueObjectUserRepositoryInterfaceUserDomainService 这一堆东西吗?

现代做法:用DDD的思想,而不是形式。关注领域逻辑,而不是分层数量。

// 简单清晰的领域模型
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @Id
    private Long id;
    private String username;
    private String email;

    @Embedded
    private Address address;  // 值对象,但不用非得叫ValueObject

    // 领域逻辑放在实体里
    public boolean canResetPassword() {
        return !isLocked() && isActive();
    }

    public void changeEmail(String newEmail) {
        validateEmail(newEmail);
        this.email = newEmail;
        this.emailVerified = false;
        // 发送验证邮件的逻辑可以放这里或服务层
    }
}

那剩下10%有用的,是什么?

我喷了90%,那剩下10%依然有价值的是什么?是一些架构思想,而不是具体的实现形式。

1. 清晰的依赖方向

有用之处:高层模块不应该依赖低层模块,两者都应该依赖抽象。

现代实践:在Spring Boot里,用接口定义Service,用实现类实现。但别为了抽象而抽象。

// 有用的抽象:定义清晰的契约
public interface PaymentService {
    PaymentResult pay(PaymentRequest request);
    RefundResult refund(RefundRequest request);
}

@Service
public class AlipayService implements PaymentService {
    // 具体实现
}

// 使用时依赖接口
@Service
public class OrderService {
    private final PaymentService paymentService;  // 依赖抽象

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

2. 策略模式的精简版

有用之处:消除if-else,让代码更清晰。

现代实践:用Map+函数式接口,或者Spring的 @Conditional

// 优雅的策略模式实现
@Service
public class PaymentStrategy {
    private final Map<String, PaymentProcessor> processors;

    public PaymentStrategy(List<PaymentProcessor> processorList) {
        processors = processorList.stream()
            .collect(Collectors.toMap(
                PaymentProcessor::getType,
                Function.identity()
            ));
    }

    public PaymentResult process(String type, PaymentRequest request) {
        PaymentProcessor processor = processors.get(type);
        if (processor == null) {
            throw new IllegalArgumentException("不支持的支付类型: " + type);
        }
        return processor.process(request);
    }
}

3. 适配器模式的实际应用

有用之处:整合第三方库,保持代码整洁。

// 适配第三方支付SDK
@Component
public class WechatPayAdapter implements PaymentProcessor {
    private final WechatPayClient wechatPayClient;

    @Override
    public PaymentResult process(PaymentRequest request) {
        // 将我们的Request转换成微信SDK的Request
        WechatPayRequest wechatRequest = convertToWechatRequest(request);

        // 调用微信SDK
        WechatPayResponse response = wechatPayClient.pay(wechatRequest);

        // 将微信Response转换成我们的Response
        return convertToPaymentResult(response);
    }
}

4. 观察者模式的事件驱动

有用之处:解耦业务逻辑。

// Spring的事件机制就很好用
@Service
public class OrderService {
    private final ApplicationEventPublisher eventPublisher;

    public Order createOrder(CreateOrderRequest request) {
        Order order = // 创建订单逻辑

        // 发布领域事件
        eventPublisher.publishEvent(new OrderCreatedEvent(this, order));

        return order;
    }
}

@Component
@Slf4j
public class OrderEventListener {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 发送邮件
        emailService.sendOrderConfirmation(event.getOrder());

        // 更新库存
        inventoryService.updateStock(event.getOrder());

        // 记录日志
        log.info("订单创建: {}", event.getOrder().getId());
    }
}

作为技术负责人,我在Code Review时的原则

  1. 能简单就别复杂:如果if-else能搞定,就别用策略模式;如果直接调用能搞定,就别用适配器。
  2. 面向变化编程,但不是面向幻想编程:为真实的需求变化做设计,而不是为想象中的“可能”需求做设计。
  3. 分层要实用,不要教条:三层架构够用就别搞四层,Service层能搞定就别非得拆出Manager、Processor、Handler。
  4. 技术选型要匹配业务规模:日活1000的系统,别用日活1000万的架构。
  5. 代码是给人看的:你写的代码,下一个接手的同事能看懂吗?能快速修改吗?

什么时候该用复杂架构?

  1. 真的遇到性能瓶颈了:数据库扛不住了,再考虑分库分表、CQRS。
  2. 业务真的复杂到需要DDD了:像电商的库存系统、金融的交易系统。
  3. 团队规模真的需要了:20人以上的团队协作,需要清晰的架构边界。
  4. 系统真的需要高可用了:99.99%的SLA要求。

记住:架构的复杂度,应该与业务的复杂度成正比,而不是与程序员的炫技欲望成正比。

现代云原生、微服务、Serverless架构,已经为我们提供了很多优秀的实践。你的目标不是写出能套用某个架构模式的代码,而是写出简单、清晰、易于维护、能快速响应业务变化的代码。

在后端开发中,克制比炫技更重要,务实比理论更重要。共勉!🚀




上一篇:Skill设计指南:基于skill-creator框架,打造高效AI Agent技能
下一篇:GitHub开源AI股票分析工具daily_stock_analysis,支持A股港股自动决策
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 19:59 , Processed in 0.440158 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表