面向微信生态的Java后端微服务架构设计:API接口的拆分与聚合策略

微信生态涵盖公众号、小程序、企业微信、支付等多个子系统,若将所有接口耦合在单体应用中,将导致部署僵化、故障扩散。本文基于wlkankan.cn.wecomwlkankan.cn.mpwlkankan.cn.pay等模块,采用领域驱动设计(DDD)进行微服务拆分,并通过BFF(Backend For Frontend)层实现接口聚合。

微服务边界划分

  • wlkankan-cn-wecom-service:企业微信组织架构、消息、审批
  • wlkankan-cn-mp-service:公众号菜单、素材、用户管理
  • wlkankan-cn-pay-service:微信支付统一下单、回调验签
  • wlkankan-cn-bff-service:面向前端的聚合网关

各服务独立数据库、独立部署,通过Feign或gRPC通信。

企业微信服务接口示例

// wlkankan-cn-wecom-service
package wlkankan.cn.wecom.api;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/wecom")
public class WeComUserController {

    @GetMapping("/user/{userId}")
    public WeComUser getUser(@PathVariable String userId, @RequestParam String corpId) {
        // 调用内部 service
        return weComUserService.fetchUser(corpId, userId);
    }

    public static class WeComUser {
        private String userId;
        private String name;
        private String department;
        // getters/setters
    }
}

在这里插入图片描述

公众号服务接口示例

// wlkankan-cn-mp-service
package wlkankan.cn.mp.api;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/mp")
public class MpMenuController {

    @PostMapping("/menu/create")
    public MenuCreateResult createMenu(@RequestBody MenuSpec spec, 
                                       @RequestParam String appId) {
        return mpMenuService.create(spec, appId);
    }

    public static class MenuSpec {
        private String name;
        private String type;
        private String url;
        // ...
    }

    public static class MenuCreateResult {
        private int errcode;
        private String errmsg;
        // ...
    }
}

BFF聚合层:统一前端入口

使用Spring Cloud Gateway + WebFlux实现响应式聚合:

// wlkankan-cn-bff-service
package wlkankan.cn.bff.controller;

import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import org.springframework.web.reactive.function.client.WebClient;

@RestController
@RequestMapping("/bff/v1")
public class UnifiedWeChatController {

    private final WebClient wecomClient = WebClient.create("http://wlkankan-cn-wecom-service");
    private final WebClient mpClient = WebClient.create("http://wlkankan-cn-mp-service");

    @GetMapping("/dashboard/{corpId}/{appId}")
    public Mono<DashboardView> getDashboard(@PathVariable String corpId,
                                            @PathVariable String appId,
                                            @RequestParam String userId) {
        Mono<WeComUser> userMono = wecomClient.get()
            .uri("/api/v1/wecom/user/" + userId + "?corpId=" + corpId)
            .retrieve()
            .bodyToMono(WeComUser.class);

        Mono<MenuStatus> menuMono = mpClient.get()
            .uri("/api/v1/mp/menu/status?appId=" + appId)
            .retrieve()
            .bodyToMono(MenuStatus.class);

        return Mono.zip(userMono, menuMono)
            .map(tuple -> {
                DashboardView view = new DashboardView();
                view.setUser(tuple.getT1());
                view.setMenuStatus(tuple.getT2());
                return view;
            });
    }

    public static class DashboardView {
        private WeComUser user;
        private MenuStatus menuStatus;
        // getters/setters
    }

    public static class WeComUser {
        private String userId;
        private String name;
        // ...
    }

    public static class MenuStatus {
        private boolean exists;
        private long updateTime;
        // ...
    }
}

服务间调用封装:Feign Client

在BFF中也可使用声明式客户端:

// wlkankan-cn-bff-service
package wlkankan.cn.bff.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "wlkankan-cn-wecom-service")
public interface WeComUserClient {
    @GetMapping("/api/v1/wecom/user/{userId}")
    WeComUser getUser(@PathVariable("userId") String userId,
                      @RequestParam("corpId") String corpId);
}

错误处理与熔断

集成Resilience4j防止级联失败:

package wlkankan.cn.bff.config;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;

@Service
public class AggregationService {

    @CircuitBreaker(name = "wecom", fallbackMethod = "fallbackUser")
    public WeComUser fetchUser(String userId, String corpId) {
        return weComUserClient.getUser(userId, corpId);
    }

    private WeComUser fallbackUser(String userId, String corpId, Throwable t) {
        WeComUser user = new WeComUser();
        user.setUserId(userId);
        user.setName("[数据暂不可用]");
        return user;
    }
}

通过wlkankan.cn系列微服务模块的拆分与BFF聚合,系统在保持各微信子域高内聚的同时,为前端提供定制化、低延迟的复合接口,有效支撑复杂业务场景下的快速迭代与弹性伸缩。

更多推荐