ddd架构下feign远程调用的架构设计

171 阅读2分钟

一、现有的架构

本项目采取ddd架构,在复杂ddd领域驱动设计的基础上简化保留精髓:

image.png

二、微服务rpc时,存在的代码耦合问题

当subject微服务中某个方法需要调用auth微服务时,需要在subject中声明auth的feignClient,然而在auth的controller入口层,采用的为dto对象,此时需要将各种dto也在subject微服务中引入,增加了代码耦合度;

为了解决这一问题,可以通过api对外接口层,将自身的feign接口提供给外部,当subject想要进行rpc时,只需在infra基础设施层中引入auth-api即可。而auth微服务的api层和controller都需要dto对象,此时只需把dto对象留在api层,contoller的pom文件引入api层即可进行解耦。common层中的result和resultCodeEnum同理,他们只在controller中被引用,抽取到api层无影响。

三、实践

3.1 在jc-club-auth-api层中引入OpenFeign依赖

<!--OpenFeign依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.0.6</version>
</dependency>
<!--负载均衡器-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
    <version>3.0.6</version>
</dependency>

3.2 在jc-club-auth-api层 编写feignclient、抽取dto和result对象

image.png

@FeignClient(name = "jc-club-auth") //微服务名称
public interface UserFeignService {
    /**
     * 获取用户信息
     */
    @RequestMapping("/user/getUserInfo")
    Result<AuthUserDTO> getUserInfo(@RequestBody AuthUserDTO authUserDTO);
}

3.3 在subject-infra中引入auth-api

<!--auth微服务feign对外接口层-->
<dependency>
    <groupId>com.ssm</groupId>
    <artifactId>jc-club-auth-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

3.4 在subject微服务进行防腐隔离

rpc查询用户信息时返回整个AuthUserDto,而在subject微服务中,只需获取username,nickname即可, 这里单独创建UserInfo对象,作为UserFeign.getUserInfo的返回类型,进行防腐隔离。

@Data
public class UserInfo {

    private String userName;

    private String nickName;
}
@Component
public class UserRpc {

    @Resource
    private UserFeignService userFeignService;

    public UserInfo getUserInfo(String userName) {
        AuthUserDTO authUserDTO = new AuthUserDTO();
        authUserDTO.setUserName(userName);
        Result<AuthUserDTO> result = userFeignService.getUserInfo(authUserDTO);
        UserInfo userInfo = new UserInfo();
        if(!result.getSuccess()) {
            return userInfo;
        }
        AuthUserDTO data = result.getData();
        userInfo.setUserName(data.getUserName());
        userInfo.setNickName(data.getNickName());
        return userInfo;
    }
}

3.5 启动类开启feign注解

@SpringBootApplication
@ComponentScan("com.ssm")  //包扫描,在别的模块下可能也有com.ssm的包,需要
@MapperScan("com.ssm.**.mapper") //扫描mapper接口
@EnableFeignClients(clients = {UserFeignService.class}) //开启feign,指定需要加载的Client接口字节码,不然扫描不到feign容器
public class SubejctApplication {
    public static void main(String[] args) {
        SpringApplication.run(SubejctApplication.class, args);
    }
}

3.6 测试

@RestController
@RequestMapping("/subject/category")
@Log4j2
public class TestUserFeignController {
    @Resource
    private UserRpc userRpc;

    @GetMapping("/test")
    public void test() {
        UserInfo userInfo = userRpc.getUserInfo("oj6nl68fpFPRVxdHfzZZX-4GjK7U");
        System.out.println(userInfo);
    }
}

image.png

image.png